import { pgTable, serial, text, integer, timestamp, uniqueIndex, index } from 'drizzle-orm/pg-core';
import { users } from '../auth/schema';

/**
 * One named agent living in one dashboard panel — the address book that lets two chat panels on the
 * same page hand work to each other instead of routing every step through the human.
 *
 * See `docs/agent-coordination.md` for why this exists at all. The three columns that carry the design:
 *
 *   `name`         the ADDRESS. Human-assigned at dashboard setup ("frontend", "backend", "reviewer")
 *                  and unique within a dashboard. Deliberately not the panel id: `movePanel` mints a
 *                  fresh panel id when a panel is dragged, so an id-based address would break the first
 *                  time the owner rearranged the layout. A name survives that; it is also what the
 *                  agents themselves use in a prompt, which nothing else here can claim.
 *
 *   `sessionKey`   the CONTINUITY. Minted once, here, and never again — where the chat UI has always
 *                  let the server mint a throwaway uuid per connection (`websocket.ts` does
 *                  `msg.sessionId || randomUUID()`), a panel hands the same key back on every load.
 *                  That is what makes an agent addressable across a reload, an officer restart and a
 *                  night with no browser open: the claude sidecar keeps a write-through
 *                  `sessionKey → claudeSessionId` map on disk, so a reaped session resumes its own
 *                  transcript on the next turn, and `chat_session_events` replays under the same key.
 *                  No heartbeat, no held process. Idle costs nothing.
 *
 *   `handoffToken` the DOOR. A bearer token the agent presents to `POST /agent-handoff` to deliver a
 *                  message to a peer. It authenticates the SENDER — which is why no `from` field is
 *                  needed on the wire and why an agent cannot forge one. Its authority is deliberately
 *                  tiny: deliver a prompt to a named peer *in the same dashboard*, nothing else. It
 *                  cannot read, cannot create panels, and cannot name a raw sessionKey.
 *
 * `rolePrompt` is the introduction sent as the session's first turn ("you are the front-end developer;
 * your peers are …"). Roles are prompts, not features — there is no role registry and there is not
 * going to be one.
 */
export const agentPanels = pgTable(
  'agent_panels',
  {
    id: serial('id').primaryKey(),
    userId: integer('user_id')
      .notNull()
      .references(() => users.id, { onDelete: 'cascade' }),
    /** Dashboard slug as the frontend knows it. No FK: `dashboards.id` is a global PK today and is
     *  being reworked to a composite (see docs/workspace-panel-todo.md §3); coupling to it now would
     *  make that change harder for no benefit here. */
    dashboardId: text('dashboard_id').notNull(),
    /** Where this agent currently lives. Bookkeeping for the UI, NOT the address — see `name`. */
    panelId: text('panel_id').notNull(),
    name: text('name').notNull(),
    sessionKey: text('session_key').notNull(),
    handoffToken: text('handoff_token').notNull(),
    /** Home-relative working directory for this agent's session, or null for the owner's home. */
    cwd: text('cwd'),
    /** Sent as the first turn when the session is created. Null once it has been sent. */
    rolePrompt: text('role_prompt'),
    /** Null until the introduction turn has actually been delivered, so a reload cannot re-send it. */
    introducedAt: timestamp('introduced_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    // The address must resolve to exactly one agent, or a handoff is ambiguous.
    uniqueIndex('uq_agent_panels_address').on(table.userId, table.dashboardId, table.name),
    // One agent per panel, so a panel cannot end up with two sessions behind it.
    uniqueIndex('uq_agent_panels_panel').on(table.userId, table.dashboardId, table.panelId),
    uniqueIndex('uq_agent_panels_session_key').on(table.sessionKey),
    uniqueIndex('uq_agent_panels_handoff_token').on(table.handoffToken),
    index('idx_agent_panels_dashboard').on(table.userId, table.dashboardId),
  ],
);

export type AgentPanelRow = typeof agentPanels.$inferSelect;
