import type { FastifyInstance } from 'fastify';
import { mkdir, stat } from 'node:fs/promises';
import type { CreateProjectRequest, CreateProjectResponse } from '@arboretum/shared';
import type { PtyManager } from '../core/pty-manager.js';
import { gitInit, isSafeAbsolutePath } from '../core/git.js';
import { resolveProjectDir } from '../core/project.js';
import { recordAudit } from '../core/audit-log.js';
import type { Db } from '../db/index.js';

/**
 * Création d'un nouveau projet : crée le dossier `<root>/<name>` (la SEULE écriture fs autorisée
 * côté serveur en dehors du dataDir) puis lance une session dedans. `git init` optionnel.
 * Cohérent avec le modèle de sécurité : un client authentifié dispose déjà d'un terminal (RCE par
 * conception). On valide tout de même strictement `name` (anti-traversal) et on exige une racine
 * EXISTANTE (mkdir non récursif → pas de création d'arborescence arbitraire).
 *
 * POST /api/v1/projects { root, name, gitInit?, command? } → 201 { session, path, gitInitialized }
 */
export function registerProjectRoutes(app: FastifyInstance, manager: PtyManager, db: Db): void {
  app.post('/api/v1/projects', async (req, reply) => {
    const body = req.body as Partial<CreateProjectRequest> | null;
    if (!body || typeof body.root !== 'string' || !isSafeAbsolutePath(body.root)) {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'root (absolute path) is required' } });
    }
    if (typeof body.name !== 'string') {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'name is required' } });
    }
    if (body.command !== undefined && body.command !== 'claude' && body.command !== 'bash') {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'command must be claude or bash' } });
    }

    // La racine doit exister et être un répertoire (mkdir non récursif derrière).
    try {
      const st = await stat(body.root);
      if (!st.isDirectory()) {
        return reply.status(400).send({ error: { code: 'NOT_A_DIRECTORY', message: `Not a directory: ${body.root}` } });
      }
    } catch (err) {
      const code = (err as NodeJS.ErrnoException).code;
      if (code === 'ENOENT') return reply.status(404).send({ error: { code: 'NOT_FOUND', message: `No such directory: ${body.root}` } });
      if (code === 'EACCES') return reply.status(403).send({ error: { code: 'FORBIDDEN', message: `Permission denied: ${body.root}` } });
      return reply.status(400).send({ error: { code: 'BAD_PATH', message: (err as Error).message } });
    }

    let dir: string;
    try {
      dir = resolveProjectDir(body.root, body.name);
    } catch (err) {
      return reply.status((err as { statusCode?: number }).statusCode ?? 400).send({ error: { code: 'BAD_REQUEST', message: (err as Error).message } });
    }

    // mkdir non récursif : EEXIST → 409 (on n'écrase jamais un dossier existant).
    try {
      await mkdir(dir);
    } catch (err) {
      const code = (err as NodeJS.ErrnoException).code;
      if (code === 'EEXIST') return reply.status(409).send({ error: { code: 'PROJECT_EXISTS', message: `Already exists: ${dir}` } });
      if (code === 'EACCES') return reply.status(403).send({ error: { code: 'FORBIDDEN', message: `Permission denied: ${dir}` } });
      return reply.status(400).send({ error: { code: 'MKDIR_FAILED', message: (err as Error).message } });
    }

    const wantsGitInit = body.gitInit === true;
    let gitInitialized = false;
    if (wantsGitInit) {
      try {
        await gitInit(dir);
        gitInitialized = true;
      } catch {
        // Le dossier est créé et la session démarrera quand même : un échec git n'annule pas le projet.
        gitInitialized = false;
      }
    }

    try {
      const session = manager.spawn({ cwd: dir, ...(body.command ? { command: body.command } : {}) });
      recordAudit(db, {
        actor: req.authContext?.tokenId ?? 'unknown',
        action: 'project.create',
        resourceId: dir,
        details: { gitInit: gitInitialized },
      });
      const res: CreateProjectResponse = { session, path: dir, gitInitialized };
      return reply.status(201).send(res);
    } catch (err) {
      const statusCode = (err as { statusCode?: number }).statusCode ?? 500;
      return reply.status(statusCode).send({ error: { code: 'SPAWN_FAILED', message: (err as Error).message } });
    }
  });
}
