import type { Sql } from '../db.js';

interface WorkspacePaneRow {
  id: string;
  kind: string;
  chatId?: string;
  chatIds?: string[];
  activeChatIdx?: number;
}

// v2.6.5: sessions.workspace_panes widened from a bare WorkspacePane[] to a
// WorkspaceState envelope { panes, tabNumbers, nextTabNumber, closedPaneStack }.
// (See the union validator in apps/server routes/sessions.ts + normalizeWorkspaceState
// in apps/server read_tab_by_number.ts — this is the coder-side mirror.)
interface WorkspaceStateRow {
  panes: WorkspacePaneRow[];
  tabNumbers: Record<string, number>;
  nextTabNumber: number;
  closedPaneStack: unknown[];
}

// MIGRATION: the stored value may be the legacy bare array OR the envelope.
// Normalize to a full envelope so callers always read `.panes` as an array and
// write the envelope back intact (preserving tabNumbers/nextTabNumber/closedPaneStack).
export function normalizeWorkspaceState(v: unknown): WorkspaceStateRow {
  if (Array.isArray(v)) {
    return { panes: v as WorkspacePaneRow[], tabNumbers: {}, nextTabNumber: 1, closedPaneStack: [] };
  }
  if (v && typeof v === 'object' && Array.isArray((v as { panes?: unknown }).panes)) {
    const env = v as Partial<WorkspaceStateRow>;
    return {
      panes: env.panes ?? [],
      tabNumbers: env.tabNumbers ?? {},
      nextTabNumber: env.nextTabNumber ?? 1,
      closedPaneStack: env.closedPaneStack ?? [],
    };
  }
  return { panes: [], tabNumbers: {}, nextTabNumber: 1, closedPaneStack: [] };
}

function chatNameForKind(kind: string): string {
  if (kind === 'coder' || kind === 'agent') return 'BooCoder';
  if (kind === 'terminal') return 'Terminal';
  return 'Chat';
}

function activeChatIdForPane(pane: WorkspacePaneRow): string | undefined {
  const chatIds = pane.chatIds ?? [];
  const idx = pane.activeChatIdx ?? 0;
  if (idx >= 0 && idx < chatIds.length) return chatIds[idx];
  return pane.chatId;
}

/** Resolve the active chat for a workspace pane; auto-seed when empty. */
export async function resolveChatId(
  sql: Sql,
  sessionId: string,
  paneId: string,
): Promise<string | null> {
  return sql.begin(async (tx) => {
    const sessionRows = await tx<{ workspace_panes: unknown }[]>`
      SELECT workspace_panes FROM sessions WHERE id = ${sessionId} FOR UPDATE
    `;
    if (sessionRows.length === 0) return null;

    const state = normalizeWorkspaceState(sessionRows[0]!.workspace_panes);
    const panes = state.panes;
    const paneIdx = panes.findIndex((p) => p.id === paneId);
    if (paneIdx < 0) return null;

    const pane = panes[paneIdx]!;
    const existingChatId = activeChatIdForPane(pane);
    if (existingChatId) {
      const chatRows = await tx<{ id: string }[]>`
        SELECT id FROM chats
        WHERE id = ${existingChatId}
          AND session_id = ${sessionId}
          AND status = 'open'
      `;
      if (chatRows.length > 0) return existingChatId;
    }

    const [newChat] = await tx<{ id: string }[]>`
      INSERT INTO chats (session_id, name, status)
      VALUES (${sessionId}, ${chatNameForKind(pane.kind)}, 'open')
      RETURNING id
    `;
    if (!newChat) return null;

    const nextChatIds = [...(pane.chatIds ?? []), newChat.id];
    const nextActiveIdx = nextChatIds.length - 1;
    const nextPanes = panes.map((p, i) =>
      i === paneIdx
        ? {
            ...p,
            chatIds: nextChatIds,
            activeChatIdx: nextActiveIdx,
            chatId: newChat.id,
          }
        : p,
    );

    const nextState: WorkspaceStateRow = { ...state, panes: nextPanes };
    await tx`
      UPDATE sessions
      SET workspace_panes = ${tx.json(nextState as never)},
          updated_at = clock_timestamp()
      WHERE id = ${sessionId}
    `;

    return newChat.id;
  });
}
