'use client';

import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
import { useSession } from 'next-auth/react';
import axiosInstance from '@/utills/axiosInstance';
import {
  setDefaultRoles,
  getDefaultRoleList,
  getDefaultHierarchy,
  type RoleConfigRole,
  type HierarchyEntry,
} from '@/utills/constant';

// ── Types ───────────────────────────────────────────────────────────────────

export interface RoleConfig {
  /** All roles returned from the hierarchy API */
  roles: RoleConfigRole[];
  /** Hierarchy entries describing parent-child relationships */
  hierarchy: HierarchyEntry[];
  /** The role ID of the currently logged-in user */
  currentUserRole: number | null;
  /** Role IDs that are below the current user in the hierarchy */
  childRoles: number[];
  /** Role IDs that are manager-level roles */
  managerRoles: number[];
  /** Whether the config has finished loading (true even if API failed) */
  loaded: boolean;
}

interface RoleConfigContextValue extends RoleConfig {
  /** Re-fetch the hierarchy from the API */
  refresh: () => Promise<void>;
}

// ── Defaults ────────────────────────────────────────────────────────────────

/**
 * Build a complete RoleConfig from a roles list + hierarchy edges for a given
 * logged-in role. Used both for the static baseline and for the API response,
 * so behaviour is identical whether or not the hierarchy API is reachable.
 */
function buildConfig(
  roles: RoleConfigRole[],
  hierarchy: HierarchyEntry[],
  currentUserRole: number | null,
  loaded: boolean,
): RoleConfig {
  const childRoles =
    currentUserRole != null ? collectChildRoles(currentUserRole, hierarchy) : [];
  const managerRoles = collectManagerRoles(hierarchy);
  return { roles, hierarchy, currentUserRole, childRoles, managerRoles, loaded };
}

// Static baseline so role-driven UI works even before/without the API.
const defaultConfig: RoleConfig = buildConfig(
  getDefaultRoleList(),
  getDefaultHierarchy(),
  null,
  false,
);

const RoleConfigContext = createContext<RoleConfigContextValue>({
  ...defaultConfig,
  refresh: async () => {},
});

// ── Helpers ─────────────────────────────────────────────────────────────────

/**
 * Walk the hierarchy tree downward from `startRoleId` and collect all
 * descendant role IDs.
 */
function collectChildRoles(
  startRoleId: number,
  hierarchy: HierarchyEntry[],
): number[] {
  const children: number[] = [];
  const queue: number[] = [startRoleId];

  while (queue.length > 0) {
    const current = queue.shift()!;
    const directChildren = hierarchy
      .filter((h) => h.parent_role_id === current)
      .map((h) => h.child_role_id);

    for (const childId of directChildren) {
      if (!children.includes(childId)) {
        children.push(childId);
        queue.push(childId);
      }
    }
  }

  return children;
}

/**
 * Identify manager roles: any role that appears as a parent_role_id in the
 * hierarchy.
 */
function collectManagerRoles(hierarchy: HierarchyEntry[]): number[] {
  const managerIds = new Set<number>();
  for (const entry of hierarchy) {
    managerIds.add(entry.parent_role_id);
  }
  return Array.from(managerIds);
}

// ── Provider ────────────────────────────────────────────────────────────────

export function RoleConfigProvider({ children }: { children: React.ReactNode }) {
  const { data: session } = useSession();
  const [config, setConfig] = useState<RoleConfig>(defaultConfig);

  const fetchHierarchy = useCallback(async () => {
    const sessionRole = session?.user?.role?.id ?? null;
    try {
      const response = await axiosInstance.get('/api/v1/hierarchy');
      const data = response.data?.data ?? response.data;

      // Read the LIVE shape returned by getRoleConfigForApi:
      //   { role, childRoles, parentRoles, ancestorRoles, allRoles, hierarchy }
      // (`roles`/`edges` are accepted as aliases for forward-compat.)
      const roles: RoleConfigRole[] = data?.allRoles ?? data?.roles ?? [];
      const hierarchy: HierarchyEntry[] = data?.hierarchy ?? data?.edges ?? [];
      const currentUserRole: number | null = data?.role?.id ?? sessionRole;

      if (roles.length === 0) {
        // The API responded but with no roles — should not happen post-migration.
        // Surface it instead of silently masking with old hardcoded values.
        throw new Error('Hierarchy API returned no roles');
      }

      // Keep the legacy DefaultRoles object in sync with the live DB IDs so any
      // remaining DefaultRoles.* lookups also use the live values.
      setDefaultRoles(roles);

      // Prefer the server-computed childRoles (live) when present.
      const childRoles: number[] = Array.isArray(data?.childRoles)
        ? data.childRoles
        : currentUserRole != null
          ? collectChildRoles(currentUserRole, hierarchy)
          : [];
      const managerRoles = collectManagerRoles(hierarchy);

      setConfig({ roles, hierarchy, currentUserRole, childRoles, managerRoles, loaded: true });
    } catch (error) {
      // Only reached if the API is unreachable/errored. The static baseline is
      // an initial-render safety net so the screen is never blank — once the
      // backend (this branch + migrations) responds, live DB data fully
      // replaces it.
      console.error('Hierarchy API unavailable; using initial baseline:', error);
      setConfig(
        buildConfig(getDefaultRoleList(), getDefaultHierarchy(), sessionRole, true),
      );
    }
  }, [session?.user?.role?.id]);

  useEffect(() => {
    // Only fetch once a session (and therefore an auth token) is available
    if (session?.user?.accessToken) {
      fetchHierarchy();
    }
  }, [session?.user?.accessToken, fetchHierarchy]);

  const contextValue: RoleConfigContextValue = {
    ...config,
    refresh: fetchHierarchy,
  };

  return (
    <RoleConfigContext.Provider value={contextValue}>
      {children}
    </RoleConfigContext.Provider>
  );
}

// ── Hook ────────────────────────────────────────────────────────────────────

export function useRoleConfig(): RoleConfigContextValue {
  const ctx = useContext(RoleConfigContext);
  if (ctx === undefined) {
    throw new Error('useRoleConfig must be used within a RoleConfigProvider');
  }
  return ctx;
}
