'use client';

import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { authClient, AuthClientError } from './auth-client';
import { AUTH_CONFIG } from './auth-config';
import type { AuthUser } from './auth-types';

interface AuthContextValue {
  user: AuthUser | null;
  loading: boolean;
  login: (
    email: string,
    password: string,
    mfaCode?: string,
    revokeSessionId?: string,
  ) => Promise<AuthUser>;
  logout: () => Promise<void>;
  refresh: () => Promise<void>;
}

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

interface AuthProviderProps {
  initialUser?: AuthUser | null;
  children: React.ReactNode;
}

export function AuthProvider({ initialUser = null, children }: AuthProviderProps) {
  const router = useRouter();
  const [user, setUser] = useState<AuthUser | null>(initialUser);
  const [loading, setLoading] = useState<boolean>(initialUser === null);

  // On mount, hydrate from /api/auth/me if we don't already have a user.
  useEffect(() => {
    if (initialUser) return;
    let cancelled = false;
    void (async () => {
      try {
        const { user: fetched } = await authClient.me();
        if (!cancelled) setUser(fetched);
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [initialUser]);

  const login = useCallback(
    async (
      email: string,
      password: string,
      mfaCode?: string,
      revokeSessionId?: string,
    ): Promise<AuthUser> => {
      const { user: signedIn } = await authClient.login(
        email,
        password,
        mfaCode,
        revokeSessionId,
      );
      setUser(signedIn);
      router.refresh();
      return signedIn;
    },
    [router],
  );

  const logout = useCallback(async (): Promise<void> => {
    try {
      await authClient.logout();
    } catch (e) {
      // Swallow — we still want to clear local state.
      if (!(e instanceof AuthClientError)) throw e;
    }
    setUser(null);
    router.replace(AUTH_CONFIG.loginPath);
    router.refresh();
  }, [router]);

  const refresh = useCallback(async (): Promise<void> => {
    const { user: fetched } = await authClient.me();
    setUser(fetched);
  }, []);

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

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

/** Strongly-typed consumer; throws if used outside the provider. */
export function useAuth(): AuthContextValue {
  const ctx = useContext(AuthContext);
  if (!ctx) {
    throw new Error('useAuth must be used inside an <AuthProvider>');
  }
  return ctx;
}
