"use client";

import { useMemo } from "react";

import { useProfile } from "@/hooks/auth/useProfile";
import {
  permissionsForRole,
  rolePanelAccess,
} from "@/lib/constants/permissions";
import { UserRole } from "@/types/auth";
import type { AuthUser } from "@/types/auth";
import type { AccessCheck, PanelAccess, Permission } from "@/types/rbac";

export interface UsePermissionsResult {
  /** The authenticated user (undefined while loading / logged out). */
  user: AuthUser | undefined;
  role: UserRole | undefined;
  panelAccess: PanelAccess | null;
  /** Effective permission keys as a set (profile payload if present, else derived from role). */
  permissions: ReadonlySet<Permission>;
  /** True while the profile query is still resolving. */
  isLoading: boolean;
  isAuthenticated: boolean;
  /** ADMIN_SUPER bypasses every permission check (mirrors the backend guard). */
  isSuperAdmin: boolean;
  /** Has this single permission (super-admin always true). */
  can: (permission: Permission) => boolean;
  /** Has AT LEAST ONE of these permissions. */
  canAny: (permissions: Permission[]) => boolean;
  /** Has ALL of these permissions. */
  canAll: (permissions: Permission[]) => boolean;
  /** Evaluate a full AccessCheck (permission/anyOf/allOf/role/panel, AND-composed). */
  check: (access: AccessCheck) => boolean;
}

/**
 * Single source of truth for the current user's access. Built on `useProfile`;
 * everything RBAC (the `<Can>` wrapper, `<RouteGuard>`, nav filtering, `useCan`)
 * consumes this.
 *
 * Permission source, in priority order:
 *  1. `user.permissions` from the profile payload — once the backend ships it.
 *  2. Derived from `user.role` via the interim ROLE_PERMISSIONS map (today).
 * Swapping (1) in is transparent to every call site.
 */
export function usePermissions(): UsePermissionsResult {
  const { data: user, isLoading } = useProfile();

  return useMemo(() => {
    const role = user?.role;
    const isSuperAdmin = role === UserRole.ADMIN_SUPER;

    const permissionList: Permission[] = user?.permissions?.length
      ? (user.permissions as Permission[])
      : role
        ? permissionsForRole(role)
        : [];
    const permissions = new Set<Permission>(permissionList);

    const panelAccess: PanelAccess | null =
      (user?.panelAccess ?? null) || (role ? rolePanelAccess(role) : null);

    const can = (permission: Permission) =>
      isSuperAdmin || permissions.has(permission);
    const canAny = (list: Permission[]) =>
      isSuperAdmin || list.some((p) => permissions.has(p));
    const canAll = (list: Permission[]) =>
      isSuperAdmin || list.every((p) => permissions.has(p));

    const check = (access: AccessCheck): boolean => {
      const { permission, anyOf, allOf, role: roleReq, panel } = access;
      if (roleReq) {
        const allowedRoles = Array.isArray(roleReq) ? roleReq : [roleReq];
        if (!role || !allowedRoles.includes(role)) return false;
      }
      if (panel && panelAccess !== panel) return false;
      if (permission && !can(permission)) return false;
      if (allOf && allOf.length > 0 && !canAll(allOf)) return false;
      if (anyOf && anyOf.length > 0 && !canAny(anyOf)) return false;
      return true;
    };

    return {
      user,
      role,
      panelAccess,
      permissions,
      isLoading,
      isAuthenticated: !!user,
      isSuperAdmin,
      can,
      canAny,
      canAll,
      check,
    };
  }, [user, isLoading]);
}

/**
 * Convenience boolean hook for a single check — for inline conditionals and for
 * passing `disabled={!useCan(...)}` to your own controls.
 *
 *   const canVerify = useCan("provider-verify");
 *   const canManage = useCan({ anyOf: ["catalog-update", "provider-update"] });
 */
export function useCan(access: Permission | AccessCheck): boolean {
  const { can, check } = usePermissions();
  return typeof access === "string" ? can(access) : check(access);
}
