/**
 * Authentication context and provider.
 *
 * Manages the user's authenticated state and exposes `login`, `logout`, and
 * `isAuthenticated` through `useAuth()`.
 *
 * The authentication model is **cookie-based**: the backend sets a session
 * cookie (`bangui_session`) on successful login, and the frontend automatically
 * includes it in all requests via `credentials: "include"`. The backend is the
 * authority on session validity; the frontend is authenticated when the backend
 * accepts the request (returns 2xx) and not authenticated when the backend
 * rejects it (returns 401/403).
 *
 * The `isAuthenticated` state persists in `sessionStorage` to survive page
 * refreshes within the browser tab but is cleared on tab close. The session
 * cookie itself persists according to the backend's cookie settings.
 */

import {
  createContext,
  useCallback,
  useEffect,
  useMemo,
  useState,
} from "react";
import { useNavigate } from "react-router-dom";
import * as authApi from "../api/auth";
import { SESSION_EXPIRED_EVENT } from "../api/client";

// ---------------------------------------------------------------------------
// Context
// ---------------------------------------------------------------------------

export interface AuthContextValue {
  /** `true` when the backend considers the session valid. */
  isAuthenticated: boolean;
  /**
   * Authenticate with the master password.
   * Throws an `ApiError` on failure.
   */
  login: (password: string) => Promise<void>;
  /** Revoke the current session and clear local state. */
  logout: () => Promise<void>;
}

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

const IS_AUTHENTICATED_KEY = "bangui_authenticated";

// ---------------------------------------------------------------------------
// Provider
// ---------------------------------------------------------------------------

/**
 * Wraps the application and provides authentication state to all children.
 *
 * Place this inside `<FluentProvider>` and `<BrowserRouter>` so all
 * descendants can call `useAuth()`.
 */
export function AuthProvider({
  children,
}: {
  children: React.ReactNode;
}): React.JSX.Element {
  const [isAuthenticated, setIsAuthenticated] = useState<boolean>(() => {
    const stored = sessionStorage.getItem(IS_AUTHENTICATED_KEY);
    return stored === "true";
  });
  const navigate = useNavigate();

  const handleSessionExpired = useCallback((): void => {
    sessionStorage.removeItem(IS_AUTHENTICATED_KEY);
    setIsAuthenticated(false);
    navigate("/login", { replace: true });
  }, [navigate]);

  useEffect((): (() => void) => {
    window.addEventListener(SESSION_EXPIRED_EVENT, handleSessionExpired);
    return (): void => {
      window.removeEventListener(SESSION_EXPIRED_EVENT, handleSessionExpired);
    };
  }, [handleSessionExpired]);

  const login = useCallback(async (password: string): Promise<void> => {
    await authApi.login(password);
    sessionStorage.setItem(IS_AUTHENTICATED_KEY, "true");
    setIsAuthenticated(true);
  }, []);

  const logout = useCallback(async (): Promise<void> => {
    try {
      await authApi.logout();
    } finally {
      // Always clear local state even if the API call fails (e.g. expired session).
      sessionStorage.removeItem(IS_AUTHENTICATED_KEY);
      setIsAuthenticated(false);
    }
  }, []);

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

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