import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
  type ReactNode,
} from 'react';
import {
  api,
  hasStoredSession,
  type LoginInput,
  type Me,
  type RegisterInput,
} from '../lib/api';
import { getSocket, disconnectSocket } from '../lib/socket';

type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';

interface AuthContextValue {
  status: AuthStatus;
  user: Me | null;
  login: (input: LoginInput) => Promise<void>;
  register: (input: RegisterInput) => Promise<void>;
  logout: () => Promise<void>;
  refreshMe: () => Promise<void>;
}

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

export function AuthProvider({ children }: { children: ReactNode }) {
  const [status, setStatus] = useState<AuthStatus>('loading');
  const [user, setUser] = useState<Me | null>(null);

  // Bootstrap: if we have a refresh token on disk, try /auth/me
  useEffect(() => {
    let cancelled = false;

    (async () => {
      if (!(await hasStoredSession())) {
        if (!cancelled) setStatus('unauthenticated');
        return;
      }
      try {
        const me = await api.me();
        if (cancelled) return;
        setUser(me);
        setStatus('authenticated');
        // Connect WebSocket on session restore
        getSocket().catch(() => {});
      } catch {
        if (cancelled) return;
        setUser(null);
        setStatus('unauthenticated');
      }
    })();

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

  const login = useCallback(async (input: LoginInput) => {
    await api.login(input);
    const me = await api.me();
    setUser(me);
    setStatus('authenticated');
    // Connect WebSocket after login
    getSocket().catch(() => {});
  }, []);

  const register = useCallback(async (input: RegisterInput) => {
    await api.register(input);
    const me = await api.me();
    setUser(me);
    setStatus('authenticated');
  }, []);

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

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

  const value = useMemo<AuthContextValue>(
    () => ({ status, user, login, register, logout, refreshMe }),
    [status, user, login, register, logout, refreshMe],
  );

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

export function useAuth(): AuthContextValue {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>');
  return ctx;
}
