'use client';

import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { useRouter } from 'next/navigation';
import Cookies from 'js-cookie';
import { api } from '@/lib/api';
import type { User } from '@/types';

// Menu tree comes from the backend (GET /menus/), already filtered for this
// user's permissions. The server decides what's visible — the sidebar renders it.
export interface MenuNode {
  id: number;
  name: string;
  route: string;
  icon: string;
  children?: MenuNode[];
}

interface AuthState {
  user: User | null;
  menus: MenuNode[];
  /** True only while the FIRST menu fetch is in flight with nothing cached. */
  menusLoading: boolean;
  /** Re-fetch the profile (e.g. after the user edits it or enables MFA). */
  refreshUser: () => void;
}

const AuthContext = createContext<AuthState>({
  user: null,
  menus: [],
  menusLoading: true,
  refreshUser: () => {},
});

/**
 * Session cache for the sidebar tree.
 *
 * The menu list only changes when an admin edits menus or the user's role
 * permissions change, so re-fetching it on every full page load just leaves the
 * sidebar blank for a network round-trip. We paint from the cached tree
 * immediately and revalidate in the background (stale-while-revalidate).
 *
 * Only menu labels/routes/icons are cached — never the profile or permissions.
 * Cleared on sign-out (see clearMenuCache) and when the tab closes.
 */
const MENU_CACHE_KEY = 'ats.sidebar.menus.v1';

function readMenuCache(): MenuNode[] {
  if (typeof window === 'undefined') return [];
  try {
    const raw = window.sessionStorage.getItem(MENU_CACHE_KEY);
    const parsed = raw ? JSON.parse(raw) : null;
    return Array.isArray(parsed) ? parsed : [];
  } catch {
    return [];
  }
}

function writeMenuCache(menus: MenuNode[]) {
  try {
    window.sessionStorage.setItem(MENU_CACHE_KEY, JSON.stringify(menus));
  } catch {
    /* storage full / disabled — caching is best-effort only */
  }
}

/** Drop the cached sidebar tree (called on sign-out). */
export function clearMenuCache() {
  try {
    window.sessionStorage.removeItem(MENU_CACHE_KEY);
  } catch {
    /* ignore */
  }
}

/**
 * Fetches the session's profile and menu tree exactly once and shares them
 * with the persistent Sidebar/Header and every portal page. Lives in the
 * (portal) layout, so client-side navigation never re-triggers these calls.
 */
export function AuthProvider({ children }: { children: React.ReactNode }) {
  const router = useRouter();
  const [user, setUser] = useState<User | null>(null);
  // Start from the cached tree so the sidebar paints on the first frame.
  const [menus, setMenus] = useState<MenuNode[]>(() => readMenuCache());
  const [menusLoading, setMenusLoading] = useState(() => readMenuCache().length === 0);
  // Guards the one-time fetch against React StrictMode's double-invoked effect.
  const fetchedRef = useRef(false);

  const refreshUser = useCallback(() => {
    (api.get('/users/profile/') as Promise<{ data: User }>)
      .then((res) => setUser(res.data))
      .catch(() => {
        clearMenuCache();
        Cookies.remove('access_token');
        Cookies.remove('refresh_token');
        router.push('/login');
      });
  }, [router]);

  useEffect(() => {
    if (fetchedRef.current) return;   // never fetch twice per mount
    fetchedRef.current = true;

    refreshUser();
    (api.get('/menus/') as Promise<{ data?: MenuNode[] }>)
      .then((res) => {
        const next = res?.data ?? [];
        setMenus(next);
        writeMenuCache(next);
      })
      .catch(() => {
        // Keep whatever was cached; only clear when we have nothing to show.
        setMenus((prev) => (prev.length ? prev : []));
      })
      .finally(() => setMenusLoading(false));
  }, [refreshUser]);

  // Stable context value — without this every consumer of useAuth() re-renders
  // on each AuthProvider state change, even when the data it reads is unchanged.
  const value = useMemo<AuthState>(
    () => ({ user, menus, menusLoading, refreshUser }),
    [user, menus, menusLoading, refreshUser],
  );

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export const useAuth = () => useContext(AuthContext);
