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

import { isDarkModeEnabled, THEMES } from 'src/constants/env';
import { useSettings } from 'src/contexts/SettingsContext';

export type ThemeMode = 'LIGHT' | 'DARK' | 'AUTO';

// Per browser/device key (no userId): the mode persists across sessions and
// communities. user.id changes per community, so it can't be part of the key.
const STORAGE_KEY = 'hu_theme_mode';

const getSystemMode = (): 'light' | 'dark' =>
  window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';

const readStoredMode = (): ThemeMode => {
  try {
    const stored = localStorage.getItem(STORAGE_KEY);
    if (!stored) return THEMES.AUTO as ThemeMode;
    const parsed: unknown = JSON.parse(stored);
    if (
      parsed === THEMES.LIGHT ||
      parsed === THEMES.DARK ||
      parsed === THEMES.AUTO
    ) {
      return parsed as ThemeMode;
    }
    return THEMES.AUTO as ThemeMode;
  } catch {
    return THEMES.AUTO as ThemeMode;
  }
};

type ThemeModeContextValue = {
  /** Committed mode (persisted). */
  themeMode: ThemeMode;
  /** Mode shown as selected in the UI (preview if previewing, else committed). */
  selectedMode: ThemeMode;
  resolvedMode: 'light' | 'dark';
  /** Preview a mode without persisting it (live, app-wide). */
  previewThemeMode: (mode: ThemeMode) => void;
  /** Persist the current preview as the committed mode. */
  commitThemeMode: () => void;
  /** Discard the preview and revert to the committed mode. */
  resetThemeMode: () => void;
};

const ThemeModeContext = createContext<ThemeModeContextValue>({
  themeMode: THEMES.AUTO as ThemeMode,
  selectedMode: THEMES.AUTO as ThemeMode,
  resolvedMode: 'light',
  previewThemeMode: () => {},
  commitThemeMode: () => {},
  resetThemeMode: () => {},
});

export const ThemeModeProvider = ({ children }: { children: ReactNode }) => {
  const { settings, saveSettings } = useSettings();

  // Latest settings without making the sync effect depend on the whole object.
  const settingsRef = useRef(settings);
  settingsRef.current = settings;

  const [themeMode, setThemeModeState] = useState<ThemeMode>(readStoredMode);

  const [systemMode, setSystemMode] = useState<'light' | 'dark'>(getSystemMode);
  const [previewMode, setPreviewMode] = useState<ThemeMode | null>(null);

  useEffect(() => {
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
    const handler = (e: MediaQueryListEvent) => {
      setSystemMode(e.matches ? 'dark' : 'light');
    };
    mediaQuery.addEventListener('change', handler);
    return () => mediaQuery.removeEventListener('change', handler);
  }, []);

  // Mode shown/applied while the drawer is open (preview), falling back to the
  // committed mode when there's no active preview.
  const selectedMode = previewMode ?? themeMode;

  const previewThemeMode = useCallback((mode: ThemeMode) => {
    setPreviewMode(mode);
  }, []);

  const commitThemeMode = useCallback(() => {
    if (!previewMode) return;
    setThemeModeState(previewMode);
    localStorage.setItem(STORAGE_KEY, JSON.stringify(previewMode));
    setPreviewMode(null);
  }, [previewMode]);

  const resetThemeMode = useCallback(() => {
    setPreviewMode(null);
  }, []);

  const resolvedMode: 'light' | 'dark' = (() => {
    if (!isDarkModeEnabled) return 'light';
    if (selectedMode === THEMES.DARK) return 'dark';
    if (selectedMode === THEMES.LIGHT) return 'light';
    return systemMode;
  })();

  // Mirror the resolved mode into the legacy SettingsContext so the root
  // ThemeWrapper theme follows the toggle. Disabled when the flag is off so
  // production keeps its existing theme behavior untouched.
  useEffect(() => {
    if (!isDarkModeEnabled) return;
    const targetTheme = resolvedMode === 'dark' ? THEMES.DARK : THEMES.LIGHT;
    const currentSettings = settingsRef.current;
    if (currentSettings.theme !== targetTheme) {
      saveSettings({ ...currentSettings, theme: targetTheme });
    }
  }, [resolvedMode, saveSettings]);

  const value = useMemo(
    () => ({
      themeMode,
      selectedMode,
      resolvedMode,
      previewThemeMode,
      commitThemeMode,
      resetThemeMode,
    }),
    [
      themeMode,
      selectedMode,
      resolvedMode,
      previewThemeMode,
      commitThemeMode,
      resetThemeMode,
    ],
  );

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

export const useThemeModeContext = () => useContext(ThemeModeContext);
