"use client";

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
  type ReactNode,
} from "react";
import { flushSync } from "react-dom";

type Theme = "light" | "dark";

type ThemeCoords = { clientX: number; clientY: number };

type ThemeContextValue = {
  theme: Theme;
  toggleTheme: (coords?: ThemeCoords) => void;
};

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

/** Relaxed ease — soft landing, no snap at the end */
const REVEAL_EASE = "cubic-bezier(0.22, 1, 0.36, 1)";
const REVEAL_MS = 920;

function applyDomTheme(next: Theme) {
  document.documentElement.classList.toggle("dark", next === "dark");
  document.documentElement.style.colorScheme = next;
  localStorage.setItem("fliemart-theme", next);
}

function viewportSize() {
  const vv = window.visualViewport;
  const w = Math.max(
    window.innerWidth,
    document.documentElement.clientWidth,
    vv?.width ?? 0,
  );
  const h = Math.max(
    window.innerHeight,
    document.documentElement.clientHeight,
    vv?.height ?? 0,
  );
  return { w, h };
}

function fallbackSurface(theme: Theme) {
  return theme === "dark" ? "#07090f" : "#f7f8fb";
}

/**
 * Same expanding-circle theme reveal, without View Transitions snapshots.
 * Commits the new theme under a veil, then grows a transparent hole from the
 * tap point (box-shadow punch) — cheap on GPU and consistent on storefront + admin.
 */
function circleReveal(
  previous: Theme,
  x: number,
  y: number,
  radius: number,
  commit: () => void,
): Promise<void> {
  return new Promise((resolve) => {
    const oldBg =
      getComputedStyle(document.body).backgroundColor ||
      fallbackSurface(previous);

    const root = document.documentElement;
    root.classList.add("theme-animating");

    const size = Math.max(2, Math.ceil(radius * 2));
    const veil = document.createElement("div");
    veil.setAttribute("aria-hidden", "true");
    veil.className = "theme-reveal-veil";
    veil.style.cssText = [
      "position:fixed",
      `left:${x}px`,
      `top:${y}px`,
      `width:${size}px`,
      `height:${size}px`,
      "margin:0",
      "padding:0",
      "border:0",
      "border-radius:50%",
      "background:transparent",
      `box-shadow:0 0 0 100vmax ${oldBg}`,
      "transform:translate(-50%,-50%) scale(0)",
      "transform-origin:center",
      "pointer-events:none",
      "z-index:2147483646",
      "will-change:transform",
    ].join(";");

    // Cover first, then flip theme underneath — avoids a one-frame flash.
    root.appendChild(veil);
    commit();

    let cleaned = false;
    const finish = () => {
      if (cleaned) return;
      cleaned = true;
      veil.remove();
      root.classList.remove("theme-animating");
      resolve();
    };

    const start = () => {
      const anim = veil.animate(
        [
          { transform: "translate(-50%, -50%) scale(0)" },
          { transform: "translate(-50%, -50%) scale(1)" },
        ],
        {
          duration: REVEAL_MS,
          easing: REVEAL_EASE,
          fill: "forwards",
        },
      );
      void anim.finished.then(finish).catch(finish);
    };

    // Wait two frames so the new theme paints under the veil before motion starts.
    requestAnimationFrame(() => {
      requestAnimationFrame(start);
    });

    window.setTimeout(finish, REVEAL_MS + 200);
  });
}

export function ThemeProvider({ children }: { children: ReactNode }) {
  const [theme, setTheme] = useState<Theme>("light");
  const [ready, setReady] = useState(false);
  const busyRef = useRef(false);

  useEffect(() => {
    const stored = localStorage.getItem("fliemart-theme") as Theme | null;
    const preferred =
      stored ??
      (window.matchMedia("(prefers-color-scheme: dark)").matches
        ? "dark"
        : "light");
    setTheme(preferred);
    applyDomTheme(preferred);
    setReady(true);
  }, []);

  const toggleTheme = useCallback(
    (coords?: ThemeCoords) => {
      if (!ready || busyRef.current) return;
      const next: Theme = theme === "light" ? "dark" : "light";
      const { w, h } = viewportSize();

      const x = Math.min(Math.max(coords?.clientX ?? w * 0.92, 0), w);
      const y = Math.min(Math.max(coords?.clientY ?? 48, 0), h);
      const maxRadius =
        Math.hypot(Math.max(x, w - x), Math.max(y, h - y)) * 1.15;

      const commit = () => {
        flushSync(() => {
          setTheme(next);
          applyDomTheme(next);
        });
      };

      if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
        commit();
        return;
      }

      busyRef.current = true;
      void circleReveal(theme, x, y, maxRadius, commit).finally(() => {
        busyRef.current = false;
      });
    },
    [ready, theme],
  );

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) throw new Error("useTheme must be used within ThemeProvider");
  return context;
}
