import fp from "fastify-plugin";
import fastifyJwt from "@fastify/jwt";
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { eq } from "drizzle-orm";
import { schema } from "@app/database";
import type { UserRole } from "@app/shared-types";
import { errors } from "../lib/errors.js";

export interface AccessTokenPayload {
  sub: string;
  role: UserRole;
  type: "access";
  /** true endast när inloggningen gick via TOTP-steget (admin-2FA). */
  mfa?: boolean;
}

declare module "fastify" {
  interface FastifyInstance {
    authenticate: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
    requireAdmin: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
  }
  interface FastifyRequest {
    userId: string;
    userRole: UserRole;
    tokenMfa: boolean;
  }
}

export const authPlugin = fp(async (app: FastifyInstance) => {
  await app.register(fastifyJwt, {
    secret: app.config.JWT_ACCESS_SECRET,
    sign: { expiresIn: app.config.JWT_ACCESS_TTL_SECONDS },
  });

  app.decorateRequest("userId", "");
  app.decorateRequest("userRole", "user" as UserRole);
  app.decorateRequest("tokenMfa", false);

  app.decorate("authenticate", async (req: FastifyRequest) => {
    let payload: AccessTokenPayload;
    try {
      payload = await req.jwtVerify<AccessTokenPayload>();
    } catch {
      throw errors.unauthorized();
    }
    if (payload.type !== "access" || !payload.sub) throw errors.unauthorized();
    req.userId = payload.sub;
    req.userRole = payload.role;
    req.tokenMfa = payload.mfa === true;
  });

  app.decorate("requireAdmin", async (req: FastifyRequest) => {
    await app.authenticate(req, undefined as unknown as FastifyReply);
    if (req.userRole !== "admin") {
      // Dubbelkolla mot databasen – rollen i en gammal token räcker inte.
      const [user] = await app.db
        .select({ role: schema.users.role })
        .from(schema.users)
        .where(eq(schema.users.id, req.userId))
        .limit(1);
      if (user?.role !== "admin") throw errors.forbidden("Kräver admin.");
      req.userRole = "admin";
    }
    // 2FA-tvång: har kontot TOTP aktiverat måste token vara MFA-utfärdad
    // (via /v1/auth/totp-verify). En lösenords-token räcker inte för admin-API.
    if (!req.tokenMfa) {
      const [totp] = await app.db
        .select({ enabledAt: schema.adminTotp.enabledAt })
        .from(schema.adminTotp)
        .where(eq(schema.adminTotp.userId, req.userId))
        .limit(1);
      if (totp?.enabledAt) {
        throw errors.forbidden("Tvåfaktorsautentisering krävs. Logga in igen med engångskod.");
      }
    }
  });
});
