import type { FastifyInstance } from 'fastify';
import type {
  AddRepoRequest,
  CreateGroupRequest,
  GroupResponse,
  GroupsListResponse,
  UpdateGroupRequest,
} from '@arboretum/shared';
import type { GroupManager } from '../core/group-manager.js';
import type { Db } from '../db/index.js';
import { recordAudit } from '../core/audit-log.js';
import { sendManagerError } from './repos.js';

export function registerGroupRoutes(app: FastifyInstance, gm: GroupManager, db: Db): void {
  app.get('/api/v1/groups', async (): Promise<GroupsListResponse> => ({ groups: gm.listGroups() }));

  app.get('/api/v1/groups/:id', async (req, reply) => {
    const { id } = req.params as { id: string };
    try {
      return reply.send({ group: gm.getGroup(id) } satisfies GroupResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });

  app.post('/api/v1/groups', async (req, reply) => {
    const body = req.body as Partial<CreateGroupRequest> | null;
    if (!body || typeof body.label !== 'string') {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'label is required' } });
    }
    try {
      const group = gm.createGroup({
        label: body.label,
        ...(body.description !== undefined ? { description: body.description } : {}),
        ...(body.color !== undefined ? { color: body.color } : {}),
        ...(Array.isArray(body.repoIds) ? { repoIds: body.repoIds } : {}),
      });
      recordAudit(db, { actor: req.authContext?.tokenId ?? 'unknown', action: 'group.create', resourceId: group.id, details: { label: group.label } });
      return reply.status(201).send({ group } satisfies GroupResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });

  app.patch('/api/v1/groups/:id', async (req, reply) => {
    const { id } = req.params as { id: string };
    const body = (req.body as Partial<UpdateGroupRequest> | null) ?? {};
    try {
      const group = gm.updateGroup(id, {
        ...(body.label !== undefined ? { label: body.label } : {}),
        ...(body.description !== undefined ? { description: body.description } : {}),
        ...(body.color !== undefined ? { color: body.color } : {}),
      });
      recordAudit(db, { actor: req.authContext?.tokenId ?? 'unknown', action: 'group.update', resourceId: id });
      return reply.send({ group } satisfies GroupResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });

  app.delete('/api/v1/groups/:id', async (req, reply) => {
    const { id } = req.params as { id: string };
    if (!gm.deleteGroup(id)) {
      return reply.status(404).send({ error: { code: 'NOT_FOUND', message: 'No group with this id' } });
    }
    recordAudit(db, { actor: req.authContext?.tokenId ?? 'unknown', action: 'group.delete', resourceId: id });
    return reply.send({ ok: true });
  });

  app.post('/api/v1/groups/:id/repos', async (req, reply) => {
    const { id } = req.params as { id: string };
    const body = req.body as Partial<AddRepoRequest> | null;
    if (!body || typeof body.repoId !== 'string') {
      return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'repoId is required' } });
    }
    try {
      return reply.send({ group: gm.addRepo(id, body.repoId) } satisfies GroupResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });

  app.delete('/api/v1/groups/:id/repos/:repoId', async (req, reply) => {
    const { id, repoId } = req.params as { id: string; repoId: string };
    try {
      return reply.send({ group: gm.removeRepo(id, repoId) } satisfies GroupResponse);
    } catch (err) {
      return sendManagerError(reply, err);
    }
  });
}
