/**
 * write-edit-robustness #4 — checkpoint restore + list routes (coder side).
 *
 * Proxied through the apps/server `/api/coder/*` blanket forwarder (no server-side
 * change needed for new routes). Restore rewinds the session worktree to the
 * checkpoint's shadow commit, trims the transcript from the anchor message forward,
 * and resets the agent backend — see services/checkpoints.ts.
 */
import type { FastifyInstance } from 'fastify';
import type { Sql } from '../db.js';
import { restoreCheckpoint, CheckpointNotFoundError } from '../services/checkpoints.js';

export function registerCheckpointRoutes(app: FastifyInstance, sql: Sql): void {
  // GET /api/sessions/:sessionId/checkpoints?chat_id= — list a chat's checkpoints
  // so the frontend can mark which messages have a restore point. When chat_id is
  // omitted, returns every checkpoint for the session's chats.
  app.get<{ Params: { sessionId: string }; Querystring: { chat_id?: string } }>(
    '/api/sessions/:sessionId/checkpoints',
    async (req, reply) => {
      const sessionId = req.params.sessionId;
      const chatId = req.query.chat_id;

      const session = await sql<{ id: string }[]>`SELECT id FROM sessions WHERE id = ${sessionId}`;
      if (session.length === 0) {
        reply.code(404);
        return { error: 'session not found' };
      }

      // Scope authoritatively through chats.session_id (always set) — NOT the
      // denormalized checkpoints.session_id (nullable). The chat_id branch must
      // still be session-gated or it's an IDOR (any session's chat_id reads its
      // checkpoints).
      const rows = chatId
        ? await sql<{ id: string; chat_id: string; message_id: string | null; label: string | null; created_at: Date }[]>`
            SELECT cp.id, cp.chat_id, cp.message_id, cp.label, cp.created_at
            FROM checkpoints cp
            JOIN chats c ON c.id = cp.chat_id
            WHERE cp.chat_id = ${chatId} AND c.session_id = ${sessionId}
            ORDER BY cp.created_at
          `
        : await sql<{ id: string; chat_id: string; message_id: string | null; label: string | null; created_at: Date }[]>`
            SELECT cp.id, cp.chat_id, cp.message_id, cp.label, cp.created_at
            FROM checkpoints cp
            JOIN chats c ON c.id = cp.chat_id
            WHERE c.session_id = ${sessionId}
            ORDER BY cp.created_at
          `;
      return rows;
    },
  );

  // POST /api/sessions/:sessionId/checkpoints/:checkpointId/restore — restore.
  app.post<{ Params: { sessionId: string; checkpointId: string } }>(
    '/api/sessions/:sessionId/checkpoints/:checkpointId/restore',
    async (req, reply) => {
      const { sessionId, checkpointId } = req.params;

      try {
        const result = await restoreCheckpoint(sql, checkpointId, {
          sessionId,
          log: app.log,
        });
        return result;
      } catch (err) {
        if (err instanceof CheckpointNotFoundError) {
          reply.code(404);
          return { error: err.message };
        }
        throw err;
      }
    },
  );
}
