export const AUTH_COOKIE_NAME = "gpsf_auth";
export const AUTH_ROLE_COOKIE_NAME = "gpsf_role";
export const AUTH_EMAIL_COOKIE_NAME = "gpsf_email";
// export const AUTH_TOKEN_COOKIE_NAME = "gpsf_token";

export const AUTH_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 7;

// A role is the backend role name (e.g. "Administrator", "PSWG"). Typed as
// `string` so new backend roles work without changing this type. Comparisons
// are case/space-insensitive (see rolesMatch) so DB casing can't lock users out.
export type UserRole = string;

export type AuthPermission = {
  action: string;
  subject: string;
};

// Canonical backend role names.
export const ROLE_ADMINISTRATOR = "Administrator";
export const ROLE_PSWG = "PSWG";
export const ROLE_MINISTRY = "Ministry";
export const ROLE_CDC_GPSF = "CDC G-PSF";
export const ROLE_CDC = "CDC";
export const ROLE_CEFP = "CEFP";
function normalizeRole(role: string | undefined | null) {
  return (role ?? "").trim().toLowerCase();
}

// Case/space-insensitive role comparison.
export function rolesMatch(
  a: string | undefined | null,
  b: string | undefined | null,
) {
  const na = normalizeRole(a);
  return na.length > 0 && na === normalizeRole(b);
}

function roleIsOneOf(role: string | undefined | null, candidates: UserRole[]) {
  return candidates.some((candidate) => rolesMatch(candidate, role));
}

// Accepted role-name aliases per area: the current DB display names AND the
// legacy seed slugs ("admin"/"private_sector"/"ministry"), matched
// case/space-insensitively. Add an alias here if a role is renamed in the DB.
const ADMIN_ROLE_ALIASES: UserRole[] = [ROLE_ADMINISTRATOR, "admin"];
const PSWG_ROLE_ALIASES: UserRole[] = [
  ROLE_PSWG,
  "private_sector",
  "private sector",
];
const MINISTRY_ROLE_ALIASES: UserRole[] = [ROLE_MINISTRY, "ministry"];
const CDC_GPSF_ROLE_ALIASES: UserRole[] = [ROLE_CDC_GPSF, "cdc_g-psf"];
const CDC_ROLE_ALIASES: UserRole[] = [ROLE_CDC, "cdc"];
const CEFP_ROLE_ALIASES: UserRole[] = [ROLE_CEFP, "cefp"];
export function isAdminRole(role: string | undefined | null) {
  return roleIsOneOf(role, ADMIN_ROLE_ALIASES);
}

function isPswgRole(role: string | undefined | null) {
  return roleIsOneOf(role, PSWG_ROLE_ALIASES);
}

export function isMinistryRole(role: string | undefined | null) {
  return roleIsOneOf(role, MINISTRY_ROLE_ALIASES);
}

export function isCdcGpsfRole(role: string | undefined | null) {
  return roleIsOneOf(role, CDC_GPSF_ROLE_ALIASES);
}

export function isCdcRole(role: string | undefined | null) {
  return roleIsOneOf(role, CDC_ROLE_ALIASES);
}

export function isCefpRole(role: string | undefined | null) {
  return roleIsOneOf(role, CEFP_ROLE_ALIASES);
}

// When a user holds several roles, the first match here wins (highest access).
export const rolePriority: UserRole[] = [
  ...ADMIN_ROLE_ALIASES,
  ...MINISTRY_ROLE_ALIASES,
  ...PSWG_ROLE_ALIASES,
  ...CDC_GPSF_ROLE_ALIASES,
  ...CDC_ROLE_ALIASES,
  ...CEFP_ROLE_ALIASES,
];

// Roles allowed into each dashboard area.
// Keep dashboard routes separated even when an admin has full backend API
// permissions. This prevents an admin account from opening the PSWG or
// ministry dashboard by typing those URLs directly.
const adminRoles: UserRole[] = ADMIN_ROLE_ALIASES;
const pswgRoles: UserRole[] = PSWG_ROLE_ALIASES;
const ministryRoles: UserRole[] = MINISTRY_ROLE_ALIASES;
const cdcGpsfRoles: UserRole[] = CDC_GPSF_ROLE_ALIASES;
const cdcRoles: UserRole[] = CDC_ROLE_ALIASES;
const cefpRoles: UserRole[] = CEFP_ROLE_ALIASES;
// Personal/common pages any authenticated user may open.
const commonPaths = ["/account-setting"];

// Real route prefix -> roles permitted. Matched by prefix against pathname.
const routePermissions: { path: string; roles: UserRole[] }[] = [
  { path: "/admin", roles: adminRoles },
  { path: "/pswg", roles: pswgRoles },
  { path: "/ministry", roles: ministryRoles },
  { path: "/cdc-gpsf", roles: cdcGpsfRoles },
  { path: "/cdc", roles: cdcRoles },
  { path: "/cefp", roles: cefpRoles },
];

// Any non-empty string is a usable role (roles are backend-driven / dynamic).
export function isUserRole(value: string | undefined): value is UserRole {
  return typeof value === "string" && value.trim().length > 0;
}

// Known role -> its dashboard. Roles without a mapped dashboard return null
// (there is no default landing); such roles are treated as unable to use the app.
export function getRoleHomePath(role: UserRole): string | null {
  if (isAdminRole(role)) {
    return "/admin/dashboard";
  }

  if (isPswgRole(role)) {
    return "/pswg/dashboard/plenary";
  }

  if (isMinistryRole(role)) {
    return "/ministry/dashboard";
  }
  if (isCdcRole(role)) {
    return "/cdc/dashboard/plenary";
  }
  if (isCefpRole(role)) {
    return "/cefp/dashboard/plenary";
  }

  if (isCdcGpsfRole(role)) {
    return "/cdc-gpsf/dashboard";
  }

  return null;
}

// A role may use the app only if it maps to a dashboard (no default landing).
// The backend (CASL) still enforces real per-endpoint access.
export function canRoleUseApp(role: UserRole) {
  return isUserRole(role) && getRoleHomePath(role) !== null;
}

function normalizePermissionValue(value: string | undefined | null) {
  return (value ?? "").trim().toLowerCase();
}

// Matches the backend CASL behavior. "manage all" grants every permission,
// while permissions such as "read WorkingGroupIssue" grant one action on one
// subject.
export function hasPermission(
  permissions: AuthPermission[],
  requiredAction: string,
  requiredSubject: string,
) {
  const action = normalizePermissionValue(requiredAction);
  const subject = normalizePermissionValue(requiredSubject);

  return permissions.some((permission) => {
    const grantedAction = normalizePermissionValue(permission.action);
    const grantedSubject = normalizePermissionValue(permission.subject);

    const actionMatches =
      grantedAction === "manage" || grantedAction === action;
    const subjectMatches =
      grantedSubject === "all" || grantedSubject === subject;

    return actionMatches && subjectMatches;
  });
}

function isCommonPath(pathname: string) {
  return commonPaths.some(
    (path) => pathname === path || pathname.startsWith(`${path}/`),
  );
}

// Whether a role may access the admin area. Today only admin-equivalent roles
// hold any admin permission, so the role check is equivalent to a permission
// check; swap the body for a per-subject permission test when that changes.
export function canAccessAdminArea(role: UserRole) {
  return isAdminRole(role);
}

export function canRoleOpenPath(role: UserRole, pathname: string) {
  // Personal/common pages are open to any authenticated user.
  if (isCommonPath(pathname)) {
    return true;
  }

  const permission = routePermissions.find(
    (item) => pathname === item.path || pathname.startsWith(`${item.path}/`),
  );

  // Not a gated area -> allow (backend still enforces data access).
  if (!permission) {
    return true;
  }

  return roleIsOneOf(role, permission.roles);
}
