import { ADMIN_ROLES, SUPPLIER_ROLES } from "@/lib/constants/auth";
import { UserRole } from "@/types/auth";
import type {
  AccessCheck,
  PanelAccess,
  Permission,
  PermissionGroup,
} from "@/types/rbac";

/**
 * The full permission catalog, grouped by category — mirrors the backend seed
 * (00_code/backend/prisma/seed.ts). Useful for building the admin Role &
 * Permission management UI (the shape of `GET /rbac/permissions`) without a
 * round-trip, and as the single reference for the `Permission` union.
 */
export const PERMISSION_GROUPS: Record<PermissionGroup, Permission[]> = {
  BOOKINGS: [
    "booking-create",
    "booking-read",
    "booking-update",
    "booking-assign-tech",
    "booking-cancel",
  ],
  PROVIDERS: [
    "provider-read",
    "provider-update",
    "provider-status-update",
    "provider-verify",
  ],
  CATALOG: ["catalog-read", "catalog-update"],
  USERS: ["user-read", "user-update", "user-delete"],
  "AUDIT-LOGS": ["auditlog-read"],
  DASHBOARD: ["dashboard-read", "report-read"],
};

/** Flat list of every permission key. */
export const ALL_PERMISSIONS: Permission[] =
  Object.values(PERMISSION_GROUPS).flat();

/**
 * INTERIM role → permissions map. Mirrors the backend seed's role definitions
 * exactly (00_code/backend/prisma/seed.ts, `rolesData`).
 *
 * This is a STOP-GAP: `GET /auth/profile` does not yet return the user's
 * effective permissions, so `usePermissions` derives them from `role` via this
 * map. The day the backend adds `permissions: string[]` to the profile payload,
 * `usePermissions` prefers that field and this map becomes a fallback only — no
 * call site changes.
 *
 * `ADMIN_SUPER` holds every permission here AND additionally bypasses all checks
 * in `usePermissions`, matching the backend guard's super-admin short-circuit.
 */
export const ROLE_PERMISSIONS: Record<UserRole, Permission[]> = {
  [UserRole.ADMIN_SUPER]: [...ALL_PERMISSIONS],
  [UserRole.ADMIN_SUPPORT]: [
    "booking-read",
    "booking-update",
    "booking-cancel",
    "provider-read",
    "provider-verify",
    "provider-status-update",
    "catalog-read",
    "user-read",
    "user-update",
    "auditlog-read",
    "dashboard-read",
    "report-read",
  ],
  [UserRole.PROVIDER_OWNER]: [
    "booking-read",
    "booking-update",
    "booking-assign-tech",
    "booking-cancel",
    "provider-read",
    "provider-update",
    "catalog-read",
    "catalog-update",
    "user-read",
    "user-update",
    "user-delete",
    "auditlog-read",
    "dashboard-read",
    "report-read",
  ],
  [UserRole.PROVIDER_MANAGER]: [
    "booking-read",
    "booking-update",
    "booking-assign-tech",
    "booking-cancel",
    "provider-read",
    "catalog-read",
    "catalog-update",
    "user-read",
    "dashboard-read",
    "report-read",
  ],
  [UserRole.PROVIDER_TECHNICIAN]: ["booking-read", "booking-update"],
  // Customer never signs into this panel (backend rejects the login); listed
  // only so the map stays exhaustive over the UserRole enum.
  [UserRole.CUSTOMER]: [
    "booking-create",
    "booking-read",
    "booking-cancel",
    "provider-read",
    "catalog-read",
  ],
};

/** Permission set for a role via the interim map (empty for unknown roles). */
export function permissionsForRole(role: UserRole): Permission[] {
  return ROLE_PERMISSIONS[role] ?? [];
}

/**
 * Derive which panel a role belongs to — the frontend twin of the backend's
 * `panelAccess` computation. Used as a fallback when the profile payload doesn't
 * carry `panelAccess`. Returns null for CUSTOMER.
 */
export function rolePanelAccess(role: UserRole): PanelAccess | null {
  if (ADMIN_ROLES.includes(role)) return "ADMIN";
  if (SUPPLIER_ROLES.includes(role)) return "PROVIDER";
  return null;
}

/**
 * Evaluate an AccessCheck knowing ONLY the user's role (no profile) — used by
 * `proxy.ts` (edge middleware), where the JWT carries the role but not the
 * permission list, so permissions are derived from the role via the interim map.
 * Client code should prefer `usePermissions().check`, which uses the real
 * permissions once the backend returns them. ADMIN_SUPER passes everything.
 */
export function checkAccessForRole(
  role: UserRole,
  access: AccessCheck,
): boolean {
  const { permission, anyOf, allOf, role: roleReq, panel } = access;
  if (roleReq) {
    const allowedRoles = Array.isArray(roleReq) ? roleReq : [roleReq];
    if (!allowedRoles.includes(role)) return false;
  }
  if (panel && rolePanelAccess(role) !== panel) return false;
  const perms = permissionsForRole(role);
  const has = (p: Permission) =>
    role === UserRole.ADMIN_SUPER || perms.includes(p);
  if (permission && !has(permission)) return false;
  if (allOf && allOf.length > 0 && !allOf.every(has)) return false;
  if (anyOf && anyOf.length > 0 && !anyOf.some(has)) return false;
  return true;
}
