import type { FastifyInstance } from 'fastify';
import type { PrismaClient } from '@prisma/client';
import type { UserService } from '../services/user.service.js';
import type { RbacDefinitionService } from '../services/rbac-definition.service.js';
import { ChangeOwnPasswordSchema, ResetPasswordSchema } from '../validation/user.schema.js';
import { getAllowSelfPasswordChange } from '../bootstrap/system-settings.js';
import { grantSelfPasswordPermission } from '../bootstrap/self-password-permission.js';

export interface UserRouteDeps {
  userService: UserService;
  rbacDefinitionService: RbacDefinitionService;
  prisma: PrismaClient;
}

export function registerUserRoutes(app: FastifyInstance, deps: UserRouteDeps): void {
  const { userService: service, rbacDefinitionService, prisma } = deps;

  app.get('/api/v1/users', async () => {
    return service.list();
  });

  app.get<{ Params: { id: string } }>('/api/v1/users/:id', async (request) => {
    // Support lookup by email (contains @) or by id
    const idOrEmail = request.params.id;
    if (idOrEmail.includes('@')) {
      return service.getByEmail(idOrEmail);
    }
    return service.getById(idOrEmail);
  });

  app.post('/api/v1/users', async (request, reply) => {
    const user = await service.create(request.body);
    // Seed the default, admin-revocable self password-change permission,
    // gated by the system setting. Never fail user creation if seeding fails.
    try {
      if (await getAllowSelfPasswordChange(prisma)) {
        await grantSelfPasswordPermission(rbacDefinitionService, user);
      }
    } catch (err) {
      request.log.warn({ err, userId: user.id }, 'failed to seed self password permission');
    }
    reply.code(201);
    return user;
  });

  // ── Self-service password change ──
  // Gated by the `set-own-password` operation (global RBAC hook). Requires the
  // current password as proof — a user who forgot it must use an admin reset.
  app.post('/api/v1/users/me/password', async (request, reply) => {
    if (request.userId === undefined) {
      reply.code(401);
      return { error: 'Authentication required' };
    }
    const { currentPassword, newPassword } = ChangeOwnPasswordSchema.parse(request.body);
    const ok = await service.verifyPassword(request.userId, currentPassword);
    if (!ok) {
      reply.code(401);
      return { error: 'Current password is incorrect' };
    }
    await service.setPassword(request.userId, newPassword);
    return { success: true };
  });

  // ── Admin reset of another user's password ──
  // Gated by edit:users (admins have edit:*). No current password required.
  app.put<{ Params: { id: string } }>('/api/v1/users/:id/password', async (request) => {
    const idOrEmail = request.params.id;
    const target = idOrEmail.includes('@')
      ? await service.getByEmail(idOrEmail)
      : await service.getById(idOrEmail);
    const { newPassword } = ResetPasswordSchema.parse(request.body);
    await service.setPassword(target.id, newPassword);
    return { success: true };
  });

  app.delete<{ Params: { id: string } }>('/api/v1/users/:id', async (request, reply) => {
    // A user cannot delete their own account through the API — self bindings
    // grant password change, not account deletion. Admins delete others.
    if (request.userId !== undefined && request.userId === request.params.id) {
      reply.code(403);
      return { error: 'You cannot delete your own account' };
    }
    await service.delete(request.params.id);
    reply.code(204);
    return undefined;
  });
}
