import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import { getSessionInfo } from '../db.js';
import {
  sanitizeId,
  tmuxSessionName,
  ensureSession,
  killSession,
  hasSession,
} from '../pty/manager.js';
import { setPendingMetadata } from '../pty/registry.js';

const ParamsSchema = z.object({ sid: z.string(), pid: z.string() });
// v1.10.8c: optional cols/rows on /start so the per-pane tmux session is
// born at the right dimensions. Bodyless POSTs remain valid (Fastify's
// tolerant parser).
const StartBodySchema = z
  .object({
    cols: z.coerce.number().int().min(1).max(2000).optional(),
    rows: z.coerce.number().int().min(1).max(2000).optional(),
    description: z.string().max(500).optional(),
    parentAgent: z.string().max(100).optional(),
  })
  .partial()
  .optional();

export function registerTerminalRoutes(app: FastifyInstance, tmuxConfPath: string): void {
  // v1.10.8c: /start creates the per-pane tmux session. Idempotent — a second
  // /start on the same paneId is a no-op (hasSession returns true). The WS
  // attach handler also calls ensureSession as belt-and-suspenders, so /start
  // is technically optional, but having it as a separate step surfaces tmux
  // errors as HTTP responses (vs WS 1011 close codes).
  app.post<{
    Params: { sid: string; pid: string };
    Body: { cols?: number; rows?: number; description?: string; parentAgent?: string } | undefined;
  }>(
    '/api/term/sessions/:sid/panes/:pid/start',
    async (req, reply) => {
      const p = ParamsSchema.safeParse(req.params);
      if (!p.success) return reply.code(400).send({ error: 'bad_params' });
      const sid = sanitizeId(p.data.sid);
      const pid = sanitizeId(p.data.pid);
      if (!sid || !pid) return reply.code(400).send({ error: 'bad_id_format' });

      const b = StartBodySchema.safeParse(req.body ?? {});
      const cols = b.success ? b.data?.cols : undefined;
      const rows = b.success ? b.data?.rows : undefined;

      // Store optional metadata for the WS attach handler to consume
      if (b.success && b.data) {
        const { description, parentAgent } = b.data;
        if (description || parentAgent) {
          setPendingMetadata(pid, { description, parentAgent });
        }
      }

      const session = await getSessionInfo(sid);
      if (!session) return reply.code(404).send({ error: 'unknown_session' });

      const sessionName = tmuxSessionName(pid);

      try {
        await ensureSession(
          tmuxConfPath,
          sessionName,
          session.project_path,
          req.log,
          cols,
          rows,
        );
      } catch (err) {
        req.log.error({ err }, 'ensureSession failed');
        return reply.code(500).send({ error: 'tmux_failed' });
      }
      return reply.code(200).send({ tmux_session: sessionName });
    },
  );

  // v1.10.8c: explicit pane teardown. Frontend calls this when the user
  // intentionally closes a terminal pane (vs an implicit WS disconnect, which
  // leaves the tmux session intact for refresh-driven resume).
  app.post<{ Params: { sid: string; pid: string } }>(
    '/api/term/sessions/:sid/panes/:pid/kill',
    async (req, reply) => {
      const p = ParamsSchema.safeParse(req.params);
      if (!p.success) return reply.code(400).send({ error: 'bad_params' });
      const sid = sanitizeId(p.data.sid);
      const pid = sanitizeId(p.data.pid);
      if (!sid || !pid) return reply.code(400).send({ error: 'bad_id_format' });

      const sessionName = tmuxSessionName(pid);
      if (!(await hasSession(tmuxConfPath, sessionName))) {
        return reply.code(404).send({ error: 'unknown_pane' });
      }
      const killed = await killSession(tmuxConfPath, sessionName);
      if (!killed) return reply.code(500).send({ error: 'tmux_kill_failed' });
      return reply.code(200).send({ ok: true });
    },
  );

  // Resize endpoint removed in v1.10.8c. Resize now flows in-band via the
  // WebSocket as a `{type:"resize",cols,rows}` text frame — no more race
  // between active-PTY-map registration and HTTP POST lookup. See ws/attach.ts.
}
