import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
  type ReactNode,
} from "react";
import { formatMoney, formatPlanPrice } from "../lib/i18n/format";
import {
  ensureCatalog,
  formatMessage,
  getCatalog,
  isCatalogLoaded,
} from "../lib/i18n/messages";
import { loadUiPrefs, saveUiPrefs } from "../lib/i18n/prefs";
import type { AppCurrency, AppLocale, MessageDict } from "../lib/i18n/types";

type I18nContextValue = {
  locale: AppLocale;
  currency: AppCurrency;
  setLocale: (locale: AppLocale) => void;
  setCurrency: (currency: AppCurrency) => void;
  setPrefs: (prefs: { locale?: AppLocale; currency?: AppCurrency }) => void;
  t: (key: string, params?: Record<string, string | number>) => string;
  formatMoney: (amountTwd: number) => string;
  formatPlanPrice: (amountTwd: number) => string;
};

const I18nContext = createContext<I18nContextValue | null>(null);

export function I18nProvider({ children }: { children: ReactNode }) {
  const [locale, setLocaleState] = useState<AppLocale>(() => loadUiPrefs().locale);
  const [currency, setCurrencyState] = useState<AppCurrency>(
    () => loadUiPrefs().currency,
  );

  const [catalog, setCatalog] = useState<MessageDict>(() => getCatalog(locale));

  useEffect(() => {
    document.documentElement.lang = locale === "en" ? "en" : "zh-Hant";
  }, [locale]);

  // 非預設語系是獨立 chunk：先用手上的字典頂著，載完再換上正式的。
  useEffect(() => {
    setCatalog(getCatalog(locale));
    if (isCatalogLoaded(locale)) return;
    let cancelled = false;
    void ensureCatalog(locale).then((dict) => {
      if (!cancelled) setCatalog(dict);
    });
    return () => {
      cancelled = true;
    };
  }, [locale]);

  const setLocale = useCallback((next: AppLocale) => {
    setLocaleState(next);
    saveUiPrefs({ locale: next });
  }, []);

  const setCurrency = useCallback((next: AppCurrency) => {
    setCurrencyState(next);
    saveUiPrefs({ currency: next });
  }, []);

  const setPrefs = useCallback(
    (prefs: { locale?: AppLocale; currency?: AppCurrency }) => {
      const nextLocale = prefs.locale ?? locale;
      const nextCurrency = prefs.currency ?? currency;
      setLocaleState(nextLocale);
      setCurrencyState(nextCurrency);
      saveUiPrefs({ locale: nextLocale, currency: nextCurrency });
    },
    [locale, currency],
  );

  const t = useCallback(
    (key: string, params?: Record<string, string | number>) =>
      formatMessage(catalog, key, params),
    [catalog],
  );

  const value = useMemo<I18nContextValue>(
    () => ({
      locale,
      currency,
      setLocale,
      setCurrency,
      setPrefs,
      t,
      formatMoney: (amountTwd: number) => formatMoney(amountTwd, currency, locale),
      formatPlanPrice: (amountTwd: number) =>
        formatPlanPrice(amountTwd, currency, locale),
    }),
    [locale, currency, setLocale, setCurrency, setPrefs, t],
  );

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

export function useI18n(): I18nContextValue {
  const ctx = useContext(I18nContext);
  if (!ctx) throw new Error("useI18n must be used within I18nProvider");
  return ctx;
}
