import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
import type { ReactNode } from "react";
import { STORAGE_KEY_THEME } from "../utils/constants";

type ThemeMode = "light" | "dark";

interface ThemeProviderContext {
  colorMode: ThemeMode;
  toggleColorMode: () => void;
  setColorMode: (mode: ThemeMode) => void;
  hasExplicitPreference: boolean;
}

const ThemeModeContext = createContext<ThemeProviderContext | null>(null);

function readSavedTheme(): ThemeMode | null {
  try {
    const value = localStorage.getItem(STORAGE_KEY_THEME);
    if (value === "dark" || value === "light") {
      return value;
    }
  } catch {
    // Ignore localStorage failures.
  }
  return null;
}

function getSystemTheme(): ThemeMode {
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}

interface ThemeProviderProps {
  children: ReactNode;
}

export function ThemeProvider({ children }: ThemeProviderProps): React.JSX.Element {
  const savedTheme = readSavedTheme();
  const [colorMode, setColorModeState] = useState<ThemeMode>(() => savedTheme ?? getSystemTheme());
  const [hasExplicitPreference, setHasExplicitPreference] = useState<boolean>(() => savedTheme !== null);

  const setColorMode = useCallback((mode: ThemeMode): void => {
    try {
      localStorage.setItem(STORAGE_KEY_THEME, mode);
    } catch {
      // Ignore storage failures.
    }
    setHasExplicitPreference(true);
    setColorModeState(mode);
  }, []);

  const toggleColorMode = useCallback((): void => {
    setColorMode(colorMode === "dark" ? "light" : "dark");
  }, [colorMode, setColorMode]);

  useEffect(() => {
    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
    const handleChange = (event: MediaQueryListEvent): void => {
      if (!hasExplicitPreference) {
        setColorModeState(event.matches ? "dark" : "light");
      }
    };

    if (typeof mediaQuery.addEventListener === "function") {
      mediaQuery.addEventListener("change", handleChange);
      function cleanup(): void {
        mediaQuery.removeEventListener("change", handleChange);
      }
      return cleanup;
    }

    return undefined;
  }, [hasExplicitPreference]);

  const value = useMemo(
    () => ({ colorMode, toggleColorMode, setColorMode, hasExplicitPreference }),
    [colorMode, toggleColorMode, setColorMode, hasExplicitPreference],
  );

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

export function useThemeMode(): ThemeProviderContext {
  const context = useContext(ThemeModeContext);
  if (context != null) {
    return context;
  }

  return {
    colorMode: "light",
    toggleColorMode: (): void => {
      /* no-op when provider is missing */
    },
    setColorMode: (): void => {
      /* no-op when provider is missing */
    },
    hasExplicitPreference: false,
  };
}
