"use client";

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

type Theme = "light" | "dark";

type ThemeState = {
  theme: Theme;
  toggle: (e?: { clientX: number; clientY: number }) => void;
};

const ThemeContext = createContext<ThemeState | null>(null);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  // Dark unless this visitor has chosen otherwise — the same default
  // public/theme-init.js paints with, so the two never disagree and the
  // page doesn't flash on load.
  const [theme, setTheme] = useState<Theme>("dark");
  // Where the click happened, so the wipe starts from the button.
  const lastTogglePoint = useRef({ x: 0, y: 0 });

  useEffect(() => {
    const stored = localStorage.getItem("xrcoin-theme") as Theme | null;
    const initial: Theme = stored === "light" ? "light" : "dark";
    setTheme(initial);
    document.documentElement.classList.toggle("dark", initial === "dark");
  }, []);

  const toggle = useCallback((e?: { clientX: number; clientY: number }) => {
    if (e) lastTogglePoint.current = { x: e.clientX, y: e.clientY };
    const run = () =>
      setTheme((prev) => {
      const next = prev === "dark" ? "light" : "dark";
      localStorage.setItem("xrcoin-theme", next);
      document.documentElement.classList.toggle("dark", next === "dark");
      return next;
      });

    // A circular wipe from the button, when the browser supports it.
    const doc = document as Document & {
      startViewTransition?: (cb: () => void) => Partial<
        Record<"ready" | "finished" | "updateCallbackDone", Promise<void>>
      >;
    };
    if (!doc.startViewTransition || window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
      run();
      return;
    }

    // Two quick clicks abort the first wipe, and the aborted transition's
    // promises reject. That's expected, so swallow it — otherwise it
    // surfaces as an uncaught InvalidStateError.
    try {
      const wipe = doc.startViewTransition(run);
      for (const key of ["ready", "finished", "updateCallbackDone"] as const) {
        wipe?.[key]?.catch(() => {});
      }
    } catch {
      // No wipe, then — the colours still change.
      run();
    }
  }, []);

  return <ThemeContext.Provider value={{ theme, toggle }}>{children}</ThemeContext.Provider>;
}

export function useTheme() {
  const ctx = useContext(ThemeContext);
  if (!ctx) throw new Error("useTheme must be used inside <ThemeProvider>");
  return ctx;
}
