import {
    createContext,
    type ReactNode,
    useCallback,
    useContext,
    useEffect,
    useMemo,
    useState,
    useTransition,
} from 'react';
import {toast} from 'sonner';
import {apiAuthCaptcha, apiAuthLogin, apiAuthLogout, apiAuthMe, type ApiAuthCaptchaResponse, type LoginParams} from '@/client';
import {getApiErrorMessage} from '@/lib/api-error';

type User = {
    uid: string;
    username: string;
    display_name?: string | null;
    avatar_url?: string | null;
    has_unread_notifications: number;
};

interface UserContextValue {
    user: User | null;
    isAuthenticated: boolean;
    isLoading: boolean;
    login: (params: LoginParams) => Promise<void>;
    logout: () => Promise<void>;
    refresh: () => Promise<void>;
    /** Fetch captcha. Returns the raw API response data. */
    fetchCaptcha: () => Promise<NonNullable<ApiAuthCaptchaResponse['data']>>;
}

export const UserContext = createContext<UserContextValue | null>(null);

export function UserProvider({children}: { children: ReactNode }) {
    const [user, setUser] = useState<User | null>(null);
    const [isLoading, setIsLoading] = useState(true);
    const [isPending, startTransition] = useTransition();

    const loadUser = useCallback(async () => {
        setIsLoading(true);
        try {
            const resp = await apiAuthMe();
            setUser(resp.data?.data ?? null);
        } catch {
            setUser(null);
        } finally {
            setIsLoading(false);
        }
    }, []);

    useEffect(() => {
        loadUser();
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    const login = useCallback(async (params: LoginParams) => {
        const resp = await apiAuthLogin({ body: params });
        // Backend returns HTTP 200 with code != 0 on error — check it explicitly
        if (resp.data?.code !== 0) {
            const err: { response?: { data?: { message?: string } } } = {};
            err.response = { data: { message: resp.data?.message } };
            toast.error(getApiErrorMessage(err, 'Login failed, please try again.'));
            throw err;
        }
        await loadUser();
    }, [loadUser]);

    const logout = useCallback(async () => {
        try {
            await apiAuthLogout();
        } catch {
            // ignore server error on logout
        }
        setUser(null);
    }, []);

    const refresh = useCallback(async () => {
        startTransition(async () => {
            await loadUser();
        });
    }, [loadUser]);

    const fetchCaptcha = useCallback(async () => {
        const resp = await apiAuthCaptcha({ body: { w: 120, h: 40, dark: false, rsa: false } });
        if (!resp.data?.data) throw new Error('No captcha data');
        return resp.data.data;
    }, []);

    const value = useMemo<UserContextValue>(() => ({
        user,
        isAuthenticated: user !== null,
        isLoading: isLoading || isPending,
        login,
        logout,
        refresh,
        fetchCaptcha,
    }), [user, isLoading, isPending, login, logout, refresh, fetchCaptcha]);

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

export function useUser() {
    const ctx = useContext(UserContext);
    if (!ctx) throw new Error('useUser must be used within UserProvider');
    return ctx;
}
