import { optima } from "$lib";
import type {
  PermissionCategory,
  PermissionNode,
} from "$lib/optima-api/modules/permissions";

export type PermissionMap = Record<string, boolean> & {
  /** Set to `true` when the permission check itself failed (API error, timeout, etc.) */
  __checkFailed?: boolean;
};

/**
 * Check multiple permissions for the current user and return a map of
 * permission → boolean.  Designed to be called from any +page.server.ts
 * or +layout.server.ts load function.
 *
 * @example
 * ```ts
 * const perms = await checkPermissions(accessToken, [
 *   "company.fetch.address",
 *   "credential.create",
 * ]);
 * // perms => { "company.fetch.address": true, "credential.create": false }
 * ```
 */
export async function checkPermissions(
  accessToken: string,
  permissions: string[],
): Promise<PermissionMap> {
  if (!permissions.length) return {};

  if (!accessToken) {
    // Return all-true so UI doesn't hide features
    const map = permissions.reduce<PermissionMap>((m, p) => {
      m[p] = true;
      return m;
    }, {} as PermissionMap);
    map.__checkFailed = true;
    return map;
  }

  try {
    const result = await optima.user.checkPermissions(accessToken, permissions);

    const results: Array<{ permission: string; hasPermission: boolean }> =
      result?.data?.results ?? [];

    const map = results.reduce<PermissionMap>((m, entry) => {
      m[entry.permission] = entry.hasPermission === true;
      return m;
    }, {});

    return map;
  } catch (err: unknown) {
    console.error(
      "Permission check failed:",
      err instanceof Error ? err.message : err,
    );
    // Default every requested permission to true on failure so the UI
    // doesn't hide features that the user may actually be allowed to use.
    // The API will still enforce access if the user truly lacks permission.
    const map = permissions.reduce<PermissionMap>((m, p) => {
      m[p] = true;
      return m;
    }, {} as PermissionMap);
    map.__checkFailed = true;
    return map;
  }
}

export const resolvePermissions = checkPermissions;

/**
 * Convenience helper — returns true when a specific permission is
 * granted inside a PermissionMap.
 */
export function hasPermission(map: PermissionMap, permission: string): boolean {
  return map[permission] === true;
}

// ── Permission tree traversal helpers ────────────────────────────────────────
// Shared by CreateRoleModal, EditUserModal, and any future component that
// needs to walk the categorised permission tree returned by the API.

/**
 * Recursively collect all permission node strings from a category tree,
 * including field-level permission strings attached to each node.
 */
export function collectAllNodes(cat: PermissionCategory): string[] {
  const nodes: string[] = [];
  for (const p of cat.permissions ?? []) {
    nodes.push(p.node);
    if (p.fieldLevelPermissions) nodes.push(...p.fieldLevelPermissions);
  }
  if (cat.subCategories) {
    for (const sub of Object.values(cat.subCategories)) {
      nodes.push(...collectAllNodes(sub as PermissionCategory));
    }
  }
  return nodes;
}

/**
 * Recursively collect all `PermissionNode` objects from a category tree.
 */
export function collectAllPermNodes(cat: PermissionCategory): PermissionNode[] {
  const nodes = [...(cat.permissions ?? [])];
  if (cat.subCategories) {
    for (const sub of Object.values(cat.subCategories)) {
      nodes.push(...collectAllPermNodes(sub as PermissionCategory));
    }
  }
  return nodes;
}

/**
 * Collect all selectable strings (node + fieldLevelPermissions) from a flat
 * array of `PermissionNode` objects.
 */
export function allSelectableStrings(perms: PermissionNode[]): string[] {
  const out: string[] = [];
  for (const p of perms) {
    out.push(p.node);
    if (p.fieldLevelPermissions) out.push(...p.fieldLevelPermissions);
  }
  return out;
}
