"use client";

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
  type ReactNode,
} from "react";

import {
  type ThemeDefinition,
  darkTheme,
  getThemeOrDefault,
  isValidThemeId,
  themeToVariables,
} from "@/themes";

interface ThemeContextValue {
  /** User preference: a theme ID (e.g. "dark", "nord") or "system" */
  theme: string;
  /** The active theme's ID after resolving "system" */
  themeId: string;
  /** The full active ThemeDefinition object */
  themeDefinition: ThemeDefinition;
  /** "light" or "dark" classification of the active theme */
  resolvedTheme: "light" | "dark";
  /** Set theme by ID or "system" */
  setTheme: (theme: string) => void;
  /** Quick toggle between "dark" and "light" themes */
  toggleTheme: () => void;
}

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

const STORAGE_KEY = "mosaic-theme";

function getSystemThemeId(): "light" | "dark" {
  if (typeof window === "undefined") return "dark";
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}

function getStoredPreference(): string {
  if (typeof window === "undefined") return "system";
  const stored = localStorage.getItem(STORAGE_KEY);
  if (stored && (stored === "system" || isValidThemeId(stored))) {
    return stored;
  }
  return "system";
}

function resolveThemeId(preference: string): string {
  if (preference === "system") return getSystemThemeId();
  return preference;
}

function applyThemeVariables(themeDef: ThemeDefinition): void {
  const root = document.documentElement;
  const vars = themeToVariables(themeDef);

  for (const [prop, value] of Object.entries(vars)) {
    root.style.setProperty(prop, value);
  }

  // Set data-theme attribute for CSS selectors that depend on light/dark
  root.setAttribute("data-theme", themeDef.isDark ? "dark" : "light");
}

interface ThemeProviderProps {
  children: ReactNode;
  defaultTheme?: string;
}

export function ThemeProvider({
  children,
  defaultTheme = "system",
}: ThemeProviderProps): React.JSX.Element {
  const [preference, setPreference] = useState<string>(defaultTheme);
  const [mounted, setMounted] = useState(false);

  const themeId = useMemo(() => resolveThemeId(preference), [preference]);
  const themeDefinition = useMemo(() => getThemeOrDefault(themeId), [themeId]);
  const resolvedTheme = themeDefinition.isDark ? "dark" : "light";

  // Initialize from storage on mount
  useEffect(() => {
    setMounted(true);
    const stored = getStoredPreference();
    setPreference(stored);

    const id = resolveThemeId(stored);
    const def = getThemeOrDefault(id);
    applyThemeVariables(def);
  }, []);

  // Apply theme whenever preference changes (after mount)
  useEffect(() => {
    if (!mounted) return;
    applyThemeVariables(themeDefinition);
  }, [themeDefinition, mounted]);

  // Listen for system theme changes when preference is "system"
  useEffect(() => {
    if (!mounted || preference !== "system") return;

    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
    const handleChange = (e: MediaQueryListEvent): void => {
      const id = e.matches ? "dark" : "light";
      const def = getThemeOrDefault(id);
      applyThemeVariables(def);
      // Force re-render by updating preference to trigger useMemo recalc
      setPreference("system");
    };

    mediaQuery.addEventListener("change", handleChange);
    return (): void => {
      mediaQuery.removeEventListener("change", handleChange);
    };
  }, [preference, mounted]);

  const setTheme = useCallback((newPreference: string) => {
    setPreference(newPreference);
    localStorage.setItem(STORAGE_KEY, newPreference);
  }, []);

  const toggleTheme = useCallback(() => {
    setTheme(resolvedTheme === "dark" ? "light" : "dark");
  }, [resolvedTheme, setTheme]);

  // SSR placeholder — render children but with dark defaults
  if (!mounted) {
    return (
      <ThemeContext.Provider
        value={{
          theme: defaultTheme,
          themeId: "dark",
          themeDefinition: darkTheme,
          resolvedTheme: "dark",
          setTheme: (): void => {
            /* no-op during SSR */
          },
          toggleTheme: (): void => {
            /* no-op during SSR */
          },
        }}
      >
        {children}
      </ThemeContext.Provider>
    );
  }

  return (
    <ThemeContext.Provider
      value={{
        theme: preference,
        themeId,
        themeDefinition,
        resolvedTheme,
        setTheme,
        toggleTheme,
      }}
    >
      {children}
    </ThemeContext.Provider>
  );
}

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