"use client"; import { createContext, useContext, useEffect, useState, useCallback } from "react"; import { api } from "@/lib/api"; import { syncPlanOnLogin } from "@/lib/planSync"; import type { UserProfile } from "@/lib/types"; interface AuthCtx { user: UserProfile | null; token: string | null; favorites: string[]; planSyncStatus: "idle" | "syncing" | "synced" | "offline"; login: (email: string, password: string) => Promise; register: (email: string, password: string, name: string) => Promise; demoLogin: () => Promise; googleLogin: (idToken: string) => Promise; logout: () => void; refreshUser: () => Promise; toggleFavorite: (slug: string) => Promise; isFavorite: (slug: string) => boolean; } const AuthContext = createContext(null); export function AuthProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState(null); const [token, setToken] = useState(null); const [favorites, setFavorites] = useState([]); const [planSyncStatus, setPlanSyncStatus] = useState<"idle" | "syncing" | "synced" | "offline">("idle"); const loadFavorites = useCallback(async (t: string) => { try { const data = await api.getFavorites(t); setFavorites(data.slugs); } catch { /* ignore */ } }, []); const reconcilePlan = useCallback(async (t: string) => { setPlanSyncStatus("syncing"); try { await syncPlanOnLogin(t); setPlanSyncStatus("synced"); } catch { setPlanSyncStatus("offline"); } }, []); useEffect(() => { const saved = localStorage.getItem("nomad-token"); const savedUser = localStorage.getItem("nomad-user"); if (saved && savedUser) { setToken(saved); setUser(JSON.parse(savedUser)); loadFavorites(saved); void reconcilePlan(saved); } }, [loadFavorites, reconcilePlan]); const persist = async (t: string, u: UserProfile) => { setToken(t); setUser(u); localStorage.setItem("nomad-token", t); localStorage.setItem("nomad-user", JSON.stringify(u)); await loadFavorites(t); await reconcilePlan(t); }; const login = async (email: string, password: string) => { try { const data = await api.login(email, password); await persist(data.token, data.user); return true; } catch { return false; } }; const register = async (email: string, password: string, name: string) => { try { const data = await api.register(email, password, name); await persist(data.token, data.user); return true; } catch { return false; } }; const demoLogin = async () => { try { const data = await api.demoLogin(); await persist(data.token, data.user); return true; } catch { return false; } }; const googleLogin = async (idToken: string) => { try { const data = await api.googleLogin(idToken); await persist(data.token, data.user); return true; } catch { return false; } }; const logout = () => { setToken(null); setUser(null); setFavorites([]); setPlanSyncStatus("idle"); localStorage.removeItem("nomad-token"); localStorage.removeItem("nomad-user"); }; const refreshUser = async () => { const t = token || localStorage.getItem("nomad-token"); if (!t) return; try { const me = await api.getMe(t); setUser(me as UserProfile); localStorage.setItem("nomad-user", JSON.stringify(me)); } catch { try { const saved = localStorage.getItem("nomad-user"); if (saved) setUser(JSON.parse(saved)); } catch { /* ignore */ } } }; const toggleFavorite = async (slug: string) => { if (!token) return; try { const data = await api.toggleFavorite(slug, token); setFavorites(data.slugs); } catch { /* ignore */ } }; return ( favorites.includes(slug), }}> {children} ); } export function useAuth() { const ctx = useContext(AuthContext); if (!ctx) throw new Error("useAuth must be used within AuthProvider"); return ctx; }