import React, { createContext, useContext, useEffect, useState } from 'react'; import { apiRequest } from '@/src/lib/api'; import { fetchAdminAccessStatus } from './admin-access'; import { clearStoredSession, createSession, readStoredSession, saveStoredSession, SESSION_EVENT_NAME, } from '@/src/lib/session'; import type { AuthResponse, AuthSession, UserProfile } from '@/src/lib/types'; interface LoginPayload { username: string; password: string; } interface AuthContextValue { ready: boolean; session: AuthSession | null; user: UserProfile | null; isAdmin: boolean; login: (payload: LoginPayload) => Promise; devLogin: (username?: string) => Promise; logout: () => void; refreshProfile: () => Promise; } const AuthContext = createContext(null); function buildSession(auth: AuthResponse): AuthSession { return createSession(auth); } export function AuthProvider({ children }: { children: React.ReactNode }) { const [session, setSession] = useState(() => readStoredSession()); const [ready, setReady] = useState(false); const [isAdmin, setIsAdmin] = useState(false); useEffect(() => { const syncSession = () => { setSession(readStoredSession()); }; window.addEventListener('storage', syncSession); window.addEventListener(SESSION_EVENT_NAME, syncSession); return () => { window.removeEventListener('storage', syncSession); window.removeEventListener(SESSION_EVENT_NAME, syncSession); }; }, []); useEffect(() => { let active = true; async function hydrate() { const storedSession = readStoredSession(); if (!storedSession) { if (active) { setSession(null); setReady(true); } return; } try { const user = await apiRequest('/user/profile'); if (!active) { return; } const nextSession = { ...storedSession, user, }; saveStoredSession(nextSession); setSession(nextSession); } catch { clearStoredSession(); if (active) { setSession(null); } } finally { if (active) { setReady(true); } } } hydrate(); return () => { active = false; }; }, []); useEffect(() => { let active = true; async function syncAdminAccess() { if (!session?.token) { if (active) { setIsAdmin(false); } return; } try { const allowed = await fetchAdminAccessStatus(); if (active) { setIsAdmin(allowed); } } catch { if (active) { setIsAdmin(false); } } } syncAdminAccess(); return () => { active = false; }; }, [session?.token]); async function refreshProfile() { const currentSession = readStoredSession(); if (!currentSession) { return; } const user = await apiRequest('/user/profile'); const nextSession = { ...currentSession, user, }; saveStoredSession(nextSession); setSession(nextSession); } async function login(payload: LoginPayload) { const auth = await apiRequest('/auth/login', { method: 'POST', body: payload, }); const nextSession = buildSession(auth); saveStoredSession(nextSession); setSession(nextSession); } async function devLogin(username?: string) { const params = new URLSearchParams(); if (username?.trim()) { params.set('username', username.trim()); } const auth = await apiRequest( `/auth/dev-login${params.size ? `?${params.toString()}` : ''}`, { method: 'POST', }, ); const nextSession = buildSession(auth); saveStoredSession(nextSession); setSession(nextSession); } function logout() { clearStoredSession(); setSession(null); } return ( {children} ); } export function useAuth() { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used inside AuthProvider'); } return context; }