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

import { logEvent, setPreferredMode } from 'src/config/logging';
import { THEMES } from 'src/constants/env';
import { useSettings } from 'src/contexts/SettingsContext';
import { EventName } from 'src/types/amplitude';
import {
  readStoredThemeMode,
  THEME_MODE_STORAGE_KEY,
  type ThemeMode,
  toAnalyticsThemeMode,
} from 'src/utils/themeMode';

export type { ThemeMode };

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

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>(readStoredThemeMode);

  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;
    const previousMode = themeMode;
    setThemeModeState(previewMode);
    localStorage.setItem(THEME_MODE_STORAGE_KEY, JSON.stringify(previewMode));

    // Track the preference confirmed on save (Preferences drawer or the
    // announcement modal). The user property is updated on every save; the
    // event fires only when the mode actually changed.
    setPreferredMode(toAnalyticsThemeMode(previewMode));
    if (previewMode !== previousMode) {
      logEvent(EventName.USER_MODE_CHANGED, {
        previous_mode: toAnalyticsThemeMode(previousMode),
        selected_mode: toAnalyticsThemeMode(previewMode),
      });
    }

    setPreviewMode(null);
  }, [previewMode, themeMode]);

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

  const resolvedMode: 'light' | 'dark' = (() => {
    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.
  useEffect(() => {
    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);
