import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
import { useLocation } from "react-router";
import type { PublicUser } from "../shared/user";

const authErrors: Record<string, string> = {
  not_configured: "Discord sign-in is not configured yet.",
  invalid_state: "Your sign-in attempt expired or could not be verified. Please try again.",
  denied: "Discord sign-in was cancelled. You can try again when ready.",
  invalid_code: "Discord did not return a sign-in code. Please try again.",
  discord_unavailable: "Could not complete Discord sign-in. Please try again.",
};

function signIn() {
  let timezone = "UTC";
  try { timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC"; } catch { /* Use UTC fallback. */ }
  window.location.assign(`/api/auth/discord?${new URLSearchParams({ timezone })}`);
}

type AuthState = {
  user: PublicUser | null;
  loading: boolean;
  busy: boolean;
  accountError: string;
  error: string;
  signIn: () => void;
  signOut: () => Promise<void>;
  retry: () => void;
};

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

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<PublicUser | null>(null);
  const [loading, setLoading] = useState(true);
  const [busy, setBusy] = useState(false);
  const [accountError, setAccountError] = useState("");
  const [actionError, setActionError] = useState("");
  const [attempt, setAttempt] = useState(0);
  const { search } = useLocation();
  const signInError = authErrors[new URLSearchParams(search).get("auth_error") ?? ""];

  useEffect(() => {
    const controller = new AbortController();
    async function loadUser() {
      try {
        const response = await fetch("/api/me", { signal: controller.signal });
        if (controller.signal.aborted) return;
        if (response.status === 401) { setUser(null); return; }
        if (!response.ok) throw new Error("Could not load your account. Please try again.");
        const account: PublicUser = await response.json();
        if (!controller.signal.aborted) setUser(account);
      } catch (error) {
        if (!controller.signal.aborted) setAccountError(error instanceof Error ? error.message : "Could not load your account.");
      } finally {
        if (!controller.signal.aborted) setLoading(false);
      }
    }
    void loadUser();
    return () => controller.abort();
  }, [attempt]);

  function retry() {
    setAccountError("");
    setLoading(true);
    setAttempt((current) => current + 1);
  }

  async function signOut() {
    setBusy(true);
    setActionError("");
    try {
      const response = await fetch("/api/auth/logout", { method: "POST" });
      if (!response.ok) throw new Error("Could not sign out. Please try again.");
      setUser(null);
    } catch (error) {
      setActionError(error instanceof Error ? error.message : "Could not sign out.");
    } finally { setBusy(false); }
  }

  return (
    <AuthContext.Provider value={{ user, loading, busy, accountError, error: accountError || actionError || signInError || "", signIn, signOut, retry }}>
      {children}
    </AuthContext.Provider>
  );
}

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