import type { FastifyInstance } from 'fastify';
import type { Skill } from '@prisma/client';

import type { SkillService } from '../services/skill.service.js';
import type { IProjectRepository } from '../repositories/project.repository.js';
import type { IAgentRepository } from '../repositories/agent.repository.js';

export function registerSkillRoutes(
  app: FastifyInstance,
  service: SkillService,
  projectRepo: IProjectRepository,
  agentRepo?: IAgentRepository,
): void {
  // ── List ──
  // Filter by `?project=`, `?projectId=`, `?agent=`, or `?scope=global`.

  app.get<{ Querystring: { project?: string; projectId?: string; agent?: string; scope?: string } }>(
    '/api/v1/skills',
    async (request) => {
      const { project, projectId, agent, scope } = request.query;
      let skills: Skill[];
      if (project !== undefined) {
        const proj = await projectRepo.findByName(project);
        if (proj === null) {
          throw Object.assign(new Error(`Project not found: ${project}`), { statusCode: 404 });
        }
        skills = await service.listSkills(proj.id);
      } else if (projectId !== undefined) {
        skills = await service.listSkills(projectId);
      } else if (agent !== undefined) {
        if (!agentRepo) {
          throw Object.assign(new Error('Agent scoping not enabled'), { statusCode: 500 });
        }
        const ag = await agentRepo.findByName(agent);
        if (ag === null) {
          throw Object.assign(new Error(`Agent not found: ${agent}`), { statusCode: 404 });
        }
        skills = await service.listSkillsForAgent(ag.id);
      } else if (scope === 'global') {
        skills = await service.listGlobalSkills();
      } else {
        skills = await service.listSkills();
      }
      return skills;
    },
  );

  app.get<{ Params: { id: string } }>('/api/v1/skills/:id', async (request) => {
    return service.getSkill(request.params.id);
  });

  // ── Create / Update / Delete ──

  app.post('/api/v1/skills', async (request, reply) => {
    const body = request.body as Record<string, unknown>;
    const resolved: Record<string, unknown> = { ...body };

    if (typeof body['project'] === 'string') {
      const proj = await projectRepo.findByName(body['project']);
      if (proj === null) {
        throw Object.assign(new Error(`Project not found: ${body['project']}`), { statusCode: 404 });
      }
      resolved['projectId'] = proj.id;
      delete resolved['project'];
    }
    if (typeof body['agent'] === 'string') {
      if (!agentRepo) {
        throw Object.assign(new Error('Agent scoping not enabled'), { statusCode: 500 });
      }
      const ag = await agentRepo.findByName(body['agent']);
      if (ag === null) {
        throw Object.assign(new Error(`Agent not found: ${body['agent']}`), { statusCode: 404 });
      }
      resolved['agentId'] = ag.id;
      delete resolved['agent'];
    }
    const skill = await service.createSkill(resolved);
    reply.code(201);
    return skill;
  });

  app.put<{ Params: { id: string } }>('/api/v1/skills/:id', async (request) => {
    return service.updateSkill(request.params.id, request.body);
  });

  app.delete<{ Params: { id: string } }>('/api/v1/skills/:id', async (request, reply) => {
    await service.deleteSkill(request.params.id);
    reply.code(204);
  });

  // ── Project-scoped views ──

  app.get<{ Params: { name: string } }>('/api/v1/projects/:name/skills', async (request) => {
    const proj = await projectRepo.findByName(request.params.name);
    if (proj === null) {
      throw Object.assign(new Error(`Project not found: ${request.params.name}`), { statusCode: 404 });
    }
    return service.listSkills(proj.id);
  });

  /**
   * Compact view for `mcpctl skills sync` (PR-5). Returns metadata only —
   * no `files`, no full `content` — so the client can decide which skills
   * are stale before fetching the full body via /api/v1/skills/:id.
   */
  app.get<{ Params: { name: string } }>(
    '/api/v1/projects/:name/skills/visible',
    async (request) => {
      const proj = await projectRepo.findByName(request.params.name);
      if (proj === null) {
        throw Object.assign(new Error(`Project not found: ${request.params.name}`), { statusCode: 404 });
      }
      return service.getVisibleSkills(proj.id);
    },
  );

  // ── Agent-scoped view ──

  app.get<{ Params: { agentName: string } }>(
    '/api/v1/agents/:agentName/skills',
    async (request, reply) => {
      if (!agentRepo) {
        throw Object.assign(new Error('Agent scoping not enabled'), { statusCode: 500 });
      }
      const agent = await agentRepo.findByName(request.params.agentName);
      if (agent === null) {
        reply.code(404);
        return { error: `Agent not found: ${request.params.agentName}` };
      }
      return service.listSkillsForAgent(agent.id);
    },
  );

  // ── Restore from a revision ──
  // POST /api/v1/skills/:id/restore-revision { revisionId, note? }

  app.post<{ Params: { id: string }; Body: { revisionId: string; note?: string } }>(
    '/api/v1/skills/:id/restore-revision',
    async (request) => {
      const { revisionId, note } = request.body;
      if (!revisionId) {
        throw Object.assign(new Error('revisionId is required'), { statusCode: 400 });
      }
      return service.restoreRevisionForSkill(request.params.id, revisionId, note);
    },
  );
}
