"use client";

import * as React from "react";

export type Theme = "dark" | "light" | "system";
export type ResolvedTheme = Exclude<Theme, "system">;

const DEFAULT_STORAGE_KEY = "jason-ui-theme";
const mediaQuery = "(prefers-color-scheme: light)";

function systemTheme(): ResolvedTheme {
  return typeof window !== "undefined" && window.matchMedia(mediaQuery).matches ? "light" : "dark";
}

function resolveTheme(theme: Theme): ResolvedTheme {
  return theme === "system" ? systemTheme() : theme;
}

/** Apply a theme immediately. For persisted React state, use ThemeProvider. */
export function setTheme(theme: Theme): ResolvedTheme {
  const resolved = resolveTheme(theme);
  if (typeof document === "undefined") return resolved;
  const root = document.documentElement;
  root.classList.toggle("light", resolved === "light");
  root.classList.toggle("dark", resolved === "dark");
  root.style.colorScheme = resolved;
  return resolved;
}

/** Toggle the currently rendered mode. */
export function toggleTheme(): ResolvedTheme {
  const current = typeof document !== "undefined" && document.documentElement.classList.contains("light");
  return setTheme(current ? "dark" : "light");
}

export interface ThemeContextValue {
  theme: Theme;
  resolvedTheme: ResolvedTheme;
  setTheme: (theme: Theme) => void;
  toggleTheme: () => void;
}

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

export function useOptionalTheme() {
  return React.useContext(ThemeContext);
}

export function ThemeProvider({
  children,
  defaultTheme = "system",
  storageKey = DEFAULT_STORAGE_KEY,
}: {
  children: React.ReactNode;
  defaultTheme?: Theme;
  storageKey?: string;
}) {
  const [theme, updateTheme] = React.useState<Theme>(defaultTheme);
  const [resolvedTheme, updateResolvedTheme] = React.useState<ResolvedTheme>(() => resolveTheme(defaultTheme));

  React.useEffect(() => {
    const stored = window.localStorage.getItem(storageKey);
    if (stored === "dark" || stored === "light" || stored === "system") updateTheme(stored);
  }, [storageKey]);

  React.useEffect(() => {
    const query = window.matchMedia(mediaQuery);
    const apply = () => updateResolvedTheme(setTheme(theme));
    apply();
    if (theme !== "system") return;
    query.addEventListener("change", apply);
    return () => query.removeEventListener("change", apply);
  }, [theme]);

  const changeTheme = React.useCallback((next: Theme) => {
    window.localStorage.setItem(storageKey, next);
    updateTheme(next);
  }, [storageKey]);

  const toggle = React.useCallback(() => {
    changeTheme(resolvedTheme === "light" ? "dark" : "light");
  }, [changeTheme, resolvedTheme]);

  const value = React.useMemo(
    () => ({ theme, resolvedTheme, setTheme: changeTheme, toggleTheme: toggle }),
    [changeTheme, resolvedTheme, theme, toggle]
  );

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

export function useTheme(): ThemeContextValue {
  const context = useOptionalTheme();
  if (!context) throw new Error("useTheme must be used inside ThemeProvider");
  return context;
}

/** Inline this before app CSS to avoid a light/dark flash during hydration. */
export function ThemeScript({
  defaultTheme = "system",
  storageKey = DEFAULT_STORAGE_KEY,
}: {
  defaultTheme?: Theme;
  storageKey?: string;
}) {
  const script = `(()=>{try{const k=${JSON.stringify(storageKey)},d=${JSON.stringify(defaultTheme)},s=localStorage.getItem(k)||d,m=s==="system"?(matchMedia(${JSON.stringify(mediaQuery)}).matches?"light":"dark"):s,e=document.documentElement;e.classList.remove("light","dark");e.classList.add(m);e.style.colorScheme=m}catch{}})();`;
  return <script dangerouslySetInnerHTML={{ __html: script }} />;
}
