import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import fp from 'fastify-plugin';
import { verifyToken } from '../utils/jwt.js';
import { findAuthContext } from '../repositories/employee.repo.js';
import { UnauthorizedError } from '../utils/errors.js';

declare module 'fastify' {
  interface FastifyRequest {
    user: {
      id: string;
      role: string;
      permissions: string[];
    };
  }
}

async function authenticatePlugin(fastify: FastifyInstance) {
  fastify.decorateRequest('user', null as any);

  fastify.addHook('onRequest', async (request: FastifyRequest, _reply: FastifyReply) => {
    const excludedPaths = ['/api/health', '/api/auth/login'];
    if (excludedPaths.includes(request.url)) return;

    const token = request.cookies?.['session'];
    if (!token) throw new UnauthorizedError('Authentication required');

    let employeeId: string;
    try {
      employeeId = verifyToken(token).sub;
    } catch {
      throw new UnauthorizedError('Invalid or expired session');
    }

    // The JWT only proves *who* this session belongs to — it says nothing
    // about whether that account, its role, or that role's permissions are
    // still current. Re-resolving from the database on every request means
    // revoking access, changing permissions, or deleting/disabling an
    // employee takes effect immediately instead of only after their token
    // expires.
    const ctx = await findAuthContext(employeeId);
    if (!ctx) throw new UnauthorizedError('Account no longer has access');

    request.user = {
      id: ctx.id,
      role: ctx.role_name,
      permissions: ctx.permissions === '*' ? ['*'] : ctx.permissions,
    };
  });
}

export default fp(authenticatePlugin, {
  name: 'authenticate',
  dependencies: ['@fastify/cookie'],
});
