import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";

import { fetchAuthStatus, login as apiLogin, setToken as setClientToken, signOut } from "../api/auth";

interface AuthContextValue {
  token: string | null;
  expiresAt: string | null;
  authEnabled: boolean;
  isAuthenticated: boolean;
  loading: boolean;
  login: (username: string, password: string) => Promise<void>;
  logout: () => void;
}

const TOKEN_KEY = "contract-companion.token";
const EXPIRY_KEY = "contract-companion.tokenExpiry";

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

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [token, setTokenState] = useState<string | null>(null);
  const [expiresAt, setExpiresAt] = useState<string | null>(null);
  const [authEnabled, setAuthEnabled] = useState<boolean>(true);
  const [loading, setLoading] = useState<boolean>(true);

  useEffect(() => {
    async function init() {
      try {
        const status = await fetchAuthStatus();
        setAuthEnabled(status.enabled);

        const storedToken = localStorage.getItem(TOKEN_KEY);
        const storedExpiry = localStorage.getItem(EXPIRY_KEY);

        if (!status.enabled) {
          setTokenState(null);
          setExpiresAt(null);
          setClientToken(null);
        } else if (storedToken && storedExpiry && new Date(storedExpiry) > new Date()) {
          setTokenState(storedToken);
          setExpiresAt(storedExpiry);
          setClientToken(storedToken);
        } else {
          localStorage.removeItem(TOKEN_KEY);
          localStorage.removeItem(EXPIRY_KEY);
          setClientToken(null);
        }
      } catch {
        // assume auth is required if status endpoint fails
        setAuthEnabled(true);
      } finally {
        setLoading(false);
      }
    }

    init();
  }, []);

  const setToken = useCallback((newToken: string | null, expiry: string | null) => {
    setTokenState(newToken);
    setExpiresAt(expiry);
    setClientToken(newToken);

    if (newToken && expiry) {
      localStorage.setItem(TOKEN_KEY, newToken);
      localStorage.setItem(EXPIRY_KEY, expiry);
    } else {
      localStorage.removeItem(TOKEN_KEY);
      localStorage.removeItem(EXPIRY_KEY);
    }
  }, []);

  const login = useCallback(
    async (username: string, password: string) => {
      const result = await apiLogin(username, password);
      setToken(result.token, result.expiresAt);
    },
    [setToken]
  );

  const logout = useCallback(() => {
    signOut();
    setToken(null, null);
  }, [setToken]);

  const value = useMemo<AuthContextValue>(
    () => ({
      token,
      expiresAt,
      authEnabled,
      isAuthenticated: !authEnabled || Boolean(token),
      loading,
      login,
      logout
    }),
    [authEnabled, loading, login, logout, token, expiresAt]
  );

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

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