import { createContext, use, useCallback, useEffect, useMemo, useState } from 'react';
import type { ReactNode } from 'react';
import { ApiError, api } from '@/api/client';
import type { CurrentUser } from '@/api/types';

interface AuthState {
    user: CurrentUser | null;
    /** Vrai tant que la session n'a pas été vérifiée au démarrage. */
    loading: boolean;
    login: (email: string, password: string) => Promise<void>;
    logout: () => Promise<void>;
}

const AuthContext = createContext<AuthState | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
    const [user, setUser] = useState<CurrentUser | null>(null);
    const [loading, setLoading] = useState(true);

    // Première requête de l'application : « qui suis-je ? ». Le cookie de
    // session est déjà là ou il ne l'est pas ; rien à lire dans localStorage,
    // rien à rafraîchir. C'est le bénéfice concret du choix cookie sur JWT.
    useEffect(() => {
        let cancelled = false;

        api.me()
            .then((me) => {
                if (!cancelled) setUser(me);
            })
            .catch(() => {
                if (!cancelled) setUser(null);
            })
            .finally(() => {
                if (!cancelled) setLoading(false);
            });

        return () => {
            cancelled = true;
        };
    }, []);

    const login = useCallback(async (email: string, password: string) => {
        setUser(await api.login(email, password));
    }, []);

    const logout = useCallback(async () => {
        await api.logout();
        setUser(null);
    }, []);

    const value = useMemo<AuthState>(
        () => ({ user, loading, login, logout }),
        [user, loading, login, logout],
    );

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

export function useAuth(): AuthState {
    const context = use(AuthContext);

    if (context === null) {
        throw new Error('useAuth doit être utilisé dans un AuthProvider.');
    }

    return context;
}

/**
 * Message d'erreur destiné à l'utilisateur.
 *
 * Les messages de l'API sont déjà rédigés pour être lus : les reformuler ici
 * ferait perdre leur précision (« la variable {prenom} est absente » devient
 * « une erreur est survenue », ce qui n'aide personne).
 */
export function humanMessage(error: unknown): string {
    if (error instanceof ApiError) return error.message;
    if (error instanceof Error) return error.message;

    return 'Une erreur inattendue est survenue.';
}
