"use client";

import type { AuthUser, LoginResult } from "@domainops/shared-types";
import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useState,
} from "react";
import * as authApi from "@/lib/api/auth";
import { applyBodyClasses, bodyClassesFor } from "./bodyClasses";

interface AuthContextValue {
  user: AuthUser | null;
  isLoading: boolean;
  login: (username: string, password: string) => Promise<LoginResult>;
  completeTwoFactorChallenge: (code: string) => Promise<AuthUser>;
  logout: () => Promise<void>;
  refresh: () => Promise<void>;
}

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

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<AuthUser | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  const refresh = useCallback(async () => {
    try {
      const current = await authApi.fetchCurrentUser();
      setUser(current);
    } catch {
      setUser(null);
    } finally {
      setIsLoading(false);
    }
  }, []);

  useEffect(() => {
    // The session lives on the Laravel origin, not Next's own cookies() -
    // there's nothing to read server-side, so this has to be a client fetch
    // on mount rather than the Suspense/use() pattern Next 16 prefers.
    // eslint-disable-next-line react-hooks/set-state-in-effect
    void refresh();
  }, [refresh]);

  useEffect(() => {
    if (isLoading) return;
    applyBodyClasses(bodyClassesFor(user));
  }, [user, isLoading]);

  const login = useCallback(async (username: string, password: string) => {
    const result = await authApi.login(username, password);
    if (!("requires_2fa" in result)) {
      setUser(result);
    }
    return result;
  }, []);

  const completeTwoFactorChallenge = useCallback(async (code: string) => {
    const loggedInUser = await authApi.challengeTwoFactor(code);
    setUser(loggedInUser);
    return loggedInUser;
  }, []);

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

  return (
    <AuthContext.Provider
      value={{ user, isLoading, login, completeTwoFactorChallenge, logout, refresh }}
    >
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth(): AuthContextValue {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return context;
}
