import { NextResponse } from "next/server";

import { prisma } from "@/lib/prisma";
import { extractAuthToken, verifyJwtToken } from "@/lib/jwt";
import { normalizeAppRole, type AppRole } from "@/lib/roles";

export type AuthPrincipal = {
  id: number;
  username: string;
  email: string | null;
  role: AppRole;
};

export const APP_MODULES = [
  "dashboard",
  "alertes",
  "etat_civil",
  "contrats",
  "absences",
  "visites_medicales",
  "sanctions",
  "fiches_paie",
  "impressions",
  "autres",
  "parametres",
] as const;

export type AppModule = (typeof APP_MODULES)[number];
export type PermissionLevel = "read" | "write";

export type UserAccessProfile = {
  allSectors: boolean;
  allEstablishments: boolean;
  allCategories: boolean;
  allContractTypes: boolean;
  permissions: Record<AppModule, { canRead: boolean; canWrite: boolean }>;
  sectors: Array<{ id: number; name: string }>;
  establishments: Array<{ id: number; name: string }>;
  categories: string[];
  contractTypes: Array<{ id: number; name: string }>;
};

export type EmployeeAccessScope = {
  sectors: string[] | undefined;
  establishments: string[] | undefined;
  categories: string[] | undefined;
  contractTypes: string[] | undefined;
};

type AuthSuccess = {
  ok: true;
  principal: AuthPrincipal;
};

type AuthFailure = {
  ok: false;
  response: NextResponse;
};

export type AuthResult = AuthSuccess | AuthFailure;

type PermissionSuccess = AuthSuccess & {
  access: UserAccessProfile;
};

export type PermissionResult = PermissionSuccess | AuthFailure;

function emptyPermissions(): UserAccessProfile["permissions"] {
  return Object.fromEntries(
    APP_MODULES.map((module) => [module, { canRead: false, canWrite: false }]),
  ) as UserAccessProfile["permissions"];
}

function superAdminPermissions(): UserAccessProfile["permissions"] {
  return Object.fromEntries(
    APP_MODULES.map((module) => [module, { canRead: true, canWrite: true }]),
  ) as UserAccessProfile["permissions"];
}

export async function getUserAccessProfile(
  principal: AuthPrincipal,
): Promise<UserAccessProfile> {
  if (principal.role === "super_admin") {
    return {
      allSectors: true,
      allEstablishments: true,
      allCategories: true,
      allContractTypes: true,
      permissions: superAdminPermissions(),
      sectors: [],
      establishments: [],
      categories: [],
      contractTypes: [],
    };
  }

  const user = await prisma.auth_users.findUnique({
    where: { id: principal.id },
    select: {
      all_sectors: true,
      all_establishments: true,
      all_categories: true,
      all_contract_types: true,
      permissions: {
        select: {
          module: true,
          can_read: true,
          can_write: true,
        },
      },
      sectors: {
        select: {
          sector: {
            select: { id: true, secteurs: true },
          },
        },
      },
      establishments: {
        select: {
          establishment: {
            select: { id: true, etb: true },
          },
        },
      },
      categories: {
        select: { category: true },
      },
      contract_types: {
        select: {
          contract_type: {
            select: { id: true, tcs: true },
          },
        },
      },
    },
  });

  const permissions = emptyPermissions();

  for (const item of user?.permissions ?? []) {
    if (!APP_MODULES.includes(item.module as AppModule)) continue;

    const module = item.module as AppModule;
    permissions[module] = {
      canRead: item.can_read || item.can_write,
      canWrite: item.can_write,
    };
  }

  return {
    allSectors: user?.all_sectors ?? false,
    allEstablishments: user?.all_establishments ?? true,
    allCategories: user?.all_categories ?? true,
    allContractTypes: user?.all_contract_types ?? true,
    permissions,
    sectors: (user?.sectors ?? [])
      .filter((item) => Boolean(item.sector.secteurs?.trim()))
      .map((item) => ({
        id: item.sector.id,
        name: item.sector.secteurs!.trim(),
      })),
    establishments: (user?.establishments ?? [])
      .filter((item) => Boolean(item.establishment.etb?.trim()))
      .map((item) => ({
        id: item.establishment.id,
        name: item.establishment.etb!.trim(),
      })),
    categories: (user?.categories ?? []).map((item) => item.category.trim()),
    contractTypes: (user?.contract_types ?? [])
      .filter((item) => Boolean(item.contract_type.tcs?.trim()))
      .map((item) => ({
        id: item.contract_type.id,
        name: item.contract_type.tcs!.trim(),
      })),
  };
}

function extractCookieToken(request: Request) {
  const cookieHeader = request.headers.get("cookie");

  if (!cookieHeader) {
    return null;
  }

  const authCookie = cookieHeader
    .split(";")
    .map((item) => item.trim())
    .find((item) => item.startsWith("auth_token="));

  return authCookie?.slice("auth_token=".length) ?? null;
}

export async function getAuthenticatedPrincipal(
  request: Request,
): Promise<AuthResult> {
  const token =
    extractCookieToken(request) ??
    extractAuthToken(request.headers.get("authorization"));

  if (!token) {
    return {
      ok: false,
      response: NextResponse.json({ message: "Unauthorized" }, { status: 401 }),
    };
  }

  let username: string;

  try {
    const payload = verifyJwtToken(token);

    if (typeof payload.sub !== "string" || !payload.sub.trim()) {
      throw new Error("Invalid token subject");
    }

    username = payload.sub;
  } catch {
    return {
      ok: false,
      response: NextResponse.json({ message: "Unauthorized" }, { status: 401 }),
    };
  }

  const authUser = await prisma.auth_users.findUnique({
    where: {
      username,
    },
    select: {
      id: true,
      username: true,
      email: true,
      role: true,
      is_active: true,
    },
  });

  if (!authUser || !authUser.is_active) {
    return {
      ok: false,
      response: NextResponse.json(
        { message: "Account disabled or unavailable" },
        { status: 403 },
      ),
    };
  }

  const role = normalizeAppRole(authUser.role);

  if (!role) {
    return {
      ok: false,
      response: NextResponse.json(
        { message: "Unknown account role" },
        { status: 403 },
      ),
    };
  }

  return {
    ok: true,
    principal: {
      id: authUser.id,
      username: authUser.username,
      email: authUser.email,
      role,
    },
  };
}

export async function requireRole(
  request: Request,
  allowedRoles: readonly AppRole[],
): Promise<AuthResult> {
  const auth = await getAuthenticatedPrincipal(request);

  if (!auth.ok) {
    return auth;
  }

  if (!allowedRoles.includes(auth.principal.role)) {
    return {
      ok: false,
      response: NextResponse.json({ message: "Forbidden" }, { status: 403 }),
    };
  }

  return auth;
}

export async function requireSuperAdmin(request: Request): Promise<AuthResult> {
  return requireRole(request, ["super_admin"]);
}

export async function requirePermission(
  request: Request,
  module: AppModule,
  level: PermissionLevel = "read",
): Promise<PermissionResult> {
  const auth = await getAuthenticatedPrincipal(request);

  if (!auth.ok) return auth;

  const access = await getUserAccessProfile(auth.principal);
  const permission = access.permissions[module];
  const allowed = level === "write" ? permission.canWrite : permission.canRead;

  if (!allowed) {
    return {
      ok: false,
      response: NextResponse.json(
        {
          message: "Forbidden",
          missingPermission: `${module}:${level}`,
        },
        { status: 403 },
      ),
    };
  }

  return {
    ...auth,
    access,
  };
}

export async function requireAnyPermission(
  request: Request,
  modules: readonly AppModule[],
  level: PermissionLevel = "read",
): Promise<PermissionResult> {
  const auth = await getAuthenticatedPrincipal(request);

  if (!auth.ok) return auth;

  const access = await getUserAccessProfile(auth.principal);
  const allowed = modules.some((module) => {
    const permission = access.permissions[module];
    return level === "write" ? permission.canWrite : permission.canRead;
  });

  if (!allowed) {
    return {
      ok: false,
      response: NextResponse.json(
        { message: "Forbidden", missingPermissions: modules },
        { status: 403 },
      ),
    };
  }

  return { ...auth, access };
}

export function employeeScopeFromAccess(
  access: UserAccessProfile,
): EmployeeAccessScope {
  return {
    sectors: access.allSectors
      ? undefined
      : access.sectors.map((sector) => sector.name),
    establishments: access.allEstablishments
      ? undefined
      : access.establishments.map((establishment) => establishment.name),
    categories: access.allCategories ? undefined : access.categories,
    contractTypes: access.allContractTypes
      ? undefined
      : access.contractTypes.map((contractType) => contractType.name),
  };
}
