import { useCallback, useEffect, useState } from 'react';

import {
  getPresetColors,
  type PresetName,
  THEME_STORAGE_KEY,
  THEME_TOKEN_KEYS,
  type ThemeTokenKey,
  type ThemeTokenMap,
} from '@/lib/themePresets';

// ---------------------------------------------------------------------------
// Storage keys
// ---------------------------------------------------------------------------

const OVERRIDES_STORAGE_KEY = 'custom-theme-overrides';
const MODE_STORAGE_KEY = 'theme-mode';

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export type ThemeMode = 'light' | 'dark' | 'system';

interface ThemeManagerState {
  presetName: PresetName;
  mode: ThemeMode;
  isDark: boolean;
  overrides: Partial<ThemeTokenMap>;
}

export interface UseThemeManagerReturn extends ThemeManagerState {
  applyPreset: (name: PresetName) => void;
  setToken: (key: ThemeTokenKey, value: string) => void;
  setMode: (mode: ThemeMode) => void;
  resetTheme: () => void;
}

// ---------------------------------------------------------------------------
// Persistence helpers
// ---------------------------------------------------------------------------

function readJson<T>(key: string, fallback: T): T {
  try {
    const raw = localStorage.getItem(key);
    if (!raw) return fallback;
    return JSON.parse(raw) as T;
  } catch {
    return fallback;
  }
}

function writeJson(key: string, value: unknown) {
  try {
    localStorage.setItem(key, JSON.stringify(value));
  } catch {
    // Storage quota exceeded – silently ignore.
  }
}

// ---------------------------------------------------------------------------
// System preference
// ---------------------------------------------------------------------------

function getSystemPrefersDark(): boolean {
  return window.matchMedia('(prefers-color-scheme: dark)').matches;
}

function resolveIsDark(mode: ThemeMode): boolean {
  if (mode === 'dark') return true;
  if (mode === 'light') return false;
  return getSystemPrefersDark();
}

// ---------------------------------------------------------------------------
// DOM helpers
// ---------------------------------------------------------------------------

function toggleDarkClass(isDark: boolean) {
  const root = document.documentElement;
  if (isDark) {
    root.classList.add('dark');
  } else {
    root.classList.remove('dark');
  }
}

function applyTokensToDom(tokens: ThemeTokenMap) {
  const root = document.documentElement;
  for (const [prop, val] of Object.entries(tokens)) {
    root.style.setProperty(prop, val);
  }
}

function clearCustomTokens() {
  const root = document.documentElement;
  for (const prop of THEME_TOKEN_KEYS) {
    root.style.removeProperty(prop);
  }
}

// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------

export function useThemeManager(): UseThemeManagerReturn {
  const [state, setState] = useState<ThemeManagerState>(() => {
    const storedPreset = localStorage.getItem(THEME_STORAGE_KEY) as PresetName | null;
    const storedMode = (localStorage.getItem(MODE_STORAGE_KEY) as ThemeMode) || 'system';
    const storedOverrides = readJson<Partial<ThemeTokenMap>>(OVERRIDES_STORAGE_KEY, {});

    return {
      presetName: storedPreset ?? 'default',
      mode: storedMode,
      isDark: resolveIsDark(storedMode),
      overrides: storedOverrides,
    };
  });

  // Listen for system color-scheme changes when mode === 'system'.
  useEffect(() => {
    if (state.mode !== 'system') return;

    const mql = window.matchMedia('(prefers-color-scheme: dark)');
    function handleChange() {
      setState((prev) => ({
        ...prev,
        isDark: mql.matches,
      }));
    }
    mql.addEventListener('change', handleChange);
    return () => mql.removeEventListener('change', handleChange);
  }, [state.mode]);

  // Keep <html> dark class in sync.
  useEffect(() => {
    toggleDarkClass(state.isDark);
  }, [state.isDark]);

  // Apply CSS tokens whenever the derived theme changes.
  useEffect(() => {
    const base = getPresetColors(state.presetName, state.isDark);

    const merged: ThemeTokenMap = {
      ...(base ?? {}),
      ...state.overrides,
    } as ThemeTokenMap;

    if (Object.keys(merged).length > 0) {
      applyTokensToDom(merged);
    } else {
      clearCustomTokens();
    }
  }, [state.presetName, state.isDark, state.overrides]);

  // -------------------------------------------------------------------
  // Actions
  // -------------------------------------------------------------------

  const applyPreset = useCallback((name: PresetName) => {
    localStorage.setItem(THEME_STORAGE_KEY, name);
    localStorage.removeItem(OVERRIDES_STORAGE_KEY);
    setState((prev) => ({
      ...prev,
      presetName: name,
      overrides: {},
    }));
  }, []);

  const setToken = useCallback((key: ThemeTokenKey, value: string) => {
    setState((prev) => {
      const nextOverrides = { ...prev.overrides, [key]: value };
      writeJson(OVERRIDES_STORAGE_KEY, nextOverrides);
      localStorage.setItem(THEME_STORAGE_KEY, 'custom');
      return {
        ...prev,
        presetName: 'custom' as PresetName,
        overrides: nextOverrides,
      };
    });
  }, []);

  const setMode = useCallback((mode: ThemeMode) => {
    localStorage.setItem(MODE_STORAGE_KEY, mode);
    setState((prev) => ({
      ...prev,
      mode,
      isDark: resolveIsDark(mode),
    }));
  }, []);

  const resetTheme = useCallback(() => {
    localStorage.removeItem(THEME_STORAGE_KEY);
    localStorage.removeItem(OVERRIDES_STORAGE_KEY);
    localStorage.removeItem(MODE_STORAGE_KEY);
    clearCustomTokens();
    setState({
      presetName: 'default',
      mode: 'system',
      isDark: getSystemPrefersDark(),
      overrides: {},
    });
  }, []);

  return {
    ...state,
    applyPreset,
    setToken,
    setMode,
    resetTheme,
  };
}
