"use client";

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

type Theme = "light" | "dark" | "system";
type Resolved = "light" | "dark";

const STORAGE_KEY = "theme";
const MQ = "(prefers-color-scheme: dark)";

const ThemeContext = createContext<{
  theme: Theme;
  resolvedTheme: Resolved;
  setTheme: (t: Theme) => void;
} | null>(null);

const systemPref = (): Resolved =>
  typeof window !== "undefined" && window.matchMedia(MQ).matches
    ? "dark"
    : "light";

const apply = (r: Resolved) =>
  document.documentElement.classList.toggle("dark", r === "dark");

// Minimal theme provider (replaces next-themes, whose client-rendered <script>
// triggers a React 19 / Cache Components warning). The no-flash class is set by
// a server-rendered head script — see ThemeScript / the root layout.
// Defaults to "system": follows the OS preference until the user picks a theme.
export const ThemeProvider = ({
  children,
  defaultTheme = "system",
}: {
  children: React.ReactNode;
  defaultTheme?: Theme;
}) => {
  const [theme, setThemeState] = useState<Theme>(defaultTheme);
  const [resolvedTheme, setResolved] = useState<Resolved>("dark");

  // Adopt the stored preference on mount and resolve it.
  useEffect(() => {
    let stored: Theme | null = null;
    try {
      stored = localStorage.getItem(STORAGE_KEY) as Theme | null;
    } catch {
      // ignore
    }
    const t = stored ?? defaultTheme;
    const r = t === "system" ? systemPref() : t;
    setThemeState(t);
    setResolved(r);
    apply(r);
  }, [defaultTheme]);

  // Follow live OS changes while in "system" mode.
  useEffect(() => {
    if (theme !== "system") return;
    const mq = window.matchMedia(MQ);
    const onChange = () => {
      const r = systemPref();
      setResolved(r);
      apply(r);
    };
    mq.addEventListener("change", onChange);
    return () => mq.removeEventListener("change", onChange);
  }, [theme]);

  const setTheme = useCallback((t: Theme) => {
    const r = t === "system" ? systemPref() : t;
    setThemeState(t);
    setResolved(r);
    try {
      localStorage.setItem(STORAGE_KEY, t);
    } catch {
      // ignore
    }
    apply(r);
  }, []);

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

export const useTheme = () => {
  const ctx = useContext(ThemeContext);
  return (
    ctx ?? {
      theme: "system" as Theme,
      resolvedTheme: "dark" as Resolved,
      setTheme: () => {},
    }
  );
};

// Inline no-flash script for <head>. Runs synchronously during HTML parsing to
// set the `dark` class before first paint — using the stored preference, or the
// OS preference when unset / set to "system". Per the Next.js "preventing flash"
// guide, render `text/javascript` on the server (so it executes) and
// `text/plain` on the client (so React doesn't flag a rendered <script>);
// suppressHydrationWarning absorbs the type mismatch.
export const ThemeScript = () => (
  <script
    type={typeof window === "undefined" ? "text/javascript" : "text/plain"}
    suppressHydrationWarning
    dangerouslySetInnerHTML={{
      __html: `(function(){try{var t=localStorage.getItem('${STORAGE_KEY}');var d=window.matchMedia('${MQ}').matches;document.documentElement.classList.toggle('dark', t==='dark'||((t==='system'||!t)&&d));}catch(e){document.documentElement.classList.add('dark');}})();`,
    }}
  />
);
