import React, {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useState,
  type PropsWithChildren,
} from 'react';
import { useTranslation } from 'react-i18next';

import {
  clearAppLanguage,
  getAppLanguage,
  getDarkMode,
  getLargeText,
  getThemeName,
  setAppLanguage,
  setDarkMode,
  setLargeText,
  setThemeName,
  supportedLanguages,
  THEME_NAMES,
  type AppLanguage,
  type ThemeName,
} from '@/core/settings/app-settings';
import i18n, { syncLanguageWithDevice } from '@/i18n';
import { useColorScheme } from '@/hooks/use-color-scheme';

type AppSettingsContextValue = {
  ready: boolean;
  language: AppLanguage | null;
  hasLanguageOverride: boolean;
  languageOptions: { code: AppLanguage | 'system'; label: string }[];
  changeLanguage: (lang: AppLanguage | 'system') => Promise<void>;
  largeText: boolean;
  changeLargeText: (value: boolean) => Promise<void>;
  darkMode: boolean;
  changeDarkMode: (value: boolean) => Promise<void>;
  themeName: ThemeName;
  themeOptions: { value: ThemeName; label: string }[];
  changeThemeName: (theme: ThemeName) => Promise<void>;
};

const AppSettingsContext = createContext<AppSettingsContextValue | null>(null);

export function AppSettingsProvider({ children }: PropsWithChildren) {
  const { setColorScheme } = useColorScheme();
  const { t } = useTranslation('app');

  const [language, setLanguageState] = useState<AppLanguage | null>(null);
  const [largeText, setLargeTextState] = useState(false);
  const [darkMode, setDarkModeState] = useState(false);
  const [themeName, setThemeNameState] = useState<ThemeName>('default');
  const [ready, setReady] = useState(false);

  useEffect(() => {
    let cancelled = false;

    async function load() {
      const [lang, large, dark, theme] = await Promise.all([
        getAppLanguage(),
        getLargeText(),
        getDarkMode(),
        getThemeName(),
      ]);
      if (cancelled) return;
      setLanguageState(lang);
      setLargeTextState(large);
      setDarkModeState(dark);
      setThemeNameState(theme);
      if (dark) setColorScheme('dark');
      setReady(true);
    }

    load();
    return () => {
      cancelled = true;
    };
  }, [setColorScheme]);

  const changeLanguage = useCallback(async (lang: AppLanguage | 'system') => {
    if (lang === 'system') {
      await clearAppLanguage();
      await syncLanguageWithDevice();
      setLanguageState(null);
    } else {
      await setAppLanguage(lang);
      await i18n.changeLanguage(lang);
      setLanguageState(lang);
    }
  }, []);

  const changeLargeText = useCallback(async (value: boolean) => {
    await setLargeText(value);
    setLargeTextState(value);
  }, []);

  const changeDarkMode = useCallback(
    async (value: boolean) => {
      await setDarkMode(value);
      setDarkModeState(value);
      setColorScheme(value ? 'dark' : 'light');
    },
    [setColorScheme],
  );

  const changeThemeName = useCallback(async (theme: ThemeName) => {
    await setThemeName(theme);
    setThemeNameState(theme);
  }, []);

  const themeOptions: { value: ThemeName; label: string }[] = THEME_NAMES.map(
    (value) => ({
      value,
      label: t(`theme.${value}`),
    }),
  );

  const languageOptions: { code: AppLanguage | 'system'; label: string }[] = [
    { code: 'system', label: t('languages.system') },
    ...supportedLanguages.map((code) => ({
      code: code as AppLanguage,
      label: t(`languages.${code}`),
    })),
  ];

  const value: AppSettingsContextValue = {
    ready,
    language: (language ?? i18n.resolvedLanguage ?? 'zh') as AppLanguage,
    hasLanguageOverride: language !== null,
    languageOptions,
    changeLanguage,
    largeText,
    changeLargeText,
    darkMode,
    changeDarkMode,
    themeName,
    themeOptions,
    changeThemeName,
  };

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

export function useAppSettingsContext(): AppSettingsContextValue {
  const ctx = useContext(AppSettingsContext);
  if (!ctx) {
    throw new Error(
      'useAppSettingsContext must be used within AppSettingsProvider',
    );
  }
  return ctx;
}
