// Routes git « IDE » (P7) : diff par fichier, statut détaillé, staging sélectif, discard, fetch, pull.
// Toutes authentifiées par le hook preValidation global (/api/**), validées strictement et auditées.
// Le commit (sélectif/amend) reste sur POST /worktrees/commit (routes/worktrees.ts) pour ne pas
// dupliquer la sémantique.
import type { FastifyInstance } from 'fastify';
import type {
  WorktreeChangesResponse,
  FileDiffResponse,
  WorktreeFilesRequest,
  DiscardFilesRequest,
  FetchWorktreeRequest,
  PullWorktreeRequest,
  WorktreeResponse,
} from '@arboretum/shared';
import type { WorktreeManager } from '../core/worktree-manager.js';
import type { Db } from '../db/index.js';
import { recordAudit } from '../core/audit-log.js';
import { sendManagerError } from './repos.js';

export function registerGitRoutes(app: FastifyInstance, wt: WorktreeManager, db: Db): void {
  // Liste des fichiers modifiés d'un worktree (statut détaillé + stats +/-).
  app.get('/api/v1/repos/:id/worktrees/changes', async (req, reply) => {
    const { id } = req.params as { id: string };
    const q = req.query as { path?: string };
    if (typeof q.path !== 'string' || q.path === '') {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'path is required' } });
    }
    try {
      const { changes, truncated } = await wt.getWorktreeChanges(id, q.path);
      return reply.send({ repoId: id, path: q.path, changes, truncated } satisfies WorktreeChangesResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });

  // Diff unifié d'un fichier (staged ou non ; untracked détecté côté manager).
  app.get('/api/v1/repos/:id/worktrees/diff', async (req, reply) => {
    const { id } = req.params as { id: string };
    const q = req.query as { path?: string; file?: string; staged?: string };
    if (typeof q.path !== 'string' || q.path === '' || typeof q.file !== 'string' || q.file === '') {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'path and file are required' } });
    }
    const staged = q.staged === '1' || q.staged === 'true';
    try {
      const res = await wt.getFileDiff(id, q.path, q.file, staged);
      return reply.send(res satisfies FileDiffResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });

  // Staging sélectif.
  app.post('/api/v1/repos/:id/worktrees/stage', async (req, reply) => {
    const { id } = req.params as { id: string };
    const body = req.body as Partial<WorktreeFilesRequest> | null;
    if (!body || typeof body.path !== 'string' || !Array.isArray(body.files)) {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'path and files are required' } });
    }
    try {
      const worktree = await wt.stage(id, body.path, body.files);
      recordAudit(db, { actor: req.authContext?.tokenId ?? 'unknown', action: 'git.stage', resourceId: id, details: { path: body.path, count: body.files.length } });
      return reply.send({ worktree } satisfies WorktreeResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });

  // Désindexation sélective.
  app.post('/api/v1/repos/:id/worktrees/unstage', async (req, reply) => {
    const { id } = req.params as { id: string };
    const body = req.body as Partial<WorktreeFilesRequest> | null;
    if (!body || typeof body.path !== 'string' || !Array.isArray(body.files)) {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'path and files are required' } });
    }
    try {
      const worktree = await wt.unstage(id, body.path, body.files);
      recordAudit(db, { actor: req.authContext?.tokenId ?? 'unknown', action: 'git.unstage', resourceId: id, details: { path: body.path, count: body.files.length } });
      return reply.send({ worktree } satisfies WorktreeResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });

  // Annulation des changements locaux (restore ; clean des untracked uniquement si includeUntracked).
  app.post('/api/v1/repos/:id/worktrees/discard', async (req, reply) => {
    const { id } = req.params as { id: string };
    const body = req.body as Partial<DiscardFilesRequest> | null;
    if (!body || typeof body.path !== 'string' || !Array.isArray(body.files)) {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'path and files are required' } });
    }
    try {
      const worktree = await wt.discard(id, body.path, body.files, body.includeUntracked === true);
      recordAudit(db, { actor: req.authContext?.tokenId ?? 'unknown', action: 'git.discard', resourceId: id, details: { path: body.path, count: body.files.length, includeUntracked: body.includeUntracked === true } });
      return reply.send({ worktree } satisfies WorktreeResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });

  // Fetch (--all --prune) : actualise ahead/behind.
  app.post('/api/v1/repos/:id/worktrees/fetch', async (req, reply) => {
    const { id } = req.params as { id: string };
    const body = req.body as Partial<FetchWorktreeRequest> | null;
    if (!body || typeof body.path !== 'string') {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'path is required' } });
    }
    try {
      const worktree = await wt.fetch(id, body.path);
      recordAudit(db, { actor: req.authContext?.tokenId ?? 'unknown', action: 'git.fetch', resourceId: id, details: { path: body.path } });
      return reply.send({ worktree } satisfies WorktreeResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });

  // Pull (ff-only par défaut, rebase optionnel).
  app.post('/api/v1/repos/:id/worktrees/pull', async (req, reply) => {
    const { id } = req.params as { id: string };
    const body = req.body as Partial<PullWorktreeRequest> | null;
    if (!body || typeof body.path !== 'string') {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'path is required' } });
    }
    const mode = body.mode === 'rebase' ? 'rebase' : 'ff-only';
    try {
      const worktree = await wt.pull(id, body.path, mode);
      recordAudit(db, { actor: req.authContext?.tokenId ?? 'unknown', action: 'git.pull', resourceId: id, details: { path: body.path, mode } });
      return reply.send({ worktree } satisfies WorktreeResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });
}
