import i18n, { LOCALE_STORAGE_KEY } from "@/config/localization/i18n";
import { getStorageItem, setStorageItem } from "@/utils/storage";
import { getLocales } from "expo-localization";
import {
  PropsWithChildren,
  createContext,
  createElement,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
} from "react";

type SupportedLocale = "en" | "vi";

type I18nContextValue = {
  t: typeof i18n.t;
  locale: SupportedLocale;
  setLocale: (locale: SupportedLocale) => Promise<void>;
  isLoaded: boolean;
};

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

const SUPPORTED_LOCALES: SupportedLocale[] = ["en", "vi"];

const resolveSupportedLocale = (
  locale: string | null | undefined
): SupportedLocale => {
  if (!locale) {
    return "en";
  }

  const normalized = locale.split("-")[0]?.toLowerCase() as SupportedLocale;
  if (normalized && SUPPORTED_LOCALES.includes(normalized)) {
    return normalized;
  }

  return "en";
};

export const I18nProvider = ({ children }: PropsWithChildren<unknown>) => {
  const [locale, setLocaleState] = useState<SupportedLocale>(
    resolveSupportedLocale(i18n.locale)
  );
  const [isLoaded, setIsLoaded] = useState(false);

  useEffect(() => {
    const loadLocale = async () => {
      try {
        const savedLocale = await getStorageItem(LOCALE_STORAGE_KEY);
        const deviceLocale = getLocales()[0]?.languageCode;
        const localeToUse = resolveSupportedLocale(savedLocale ?? deviceLocale);

        if (localeToUse !== i18n.locale) {
          i18n.locale = localeToUse;
        }
        setLocaleState(localeToUse);
      } catch (error) {
        console.error("Error loading locale preference:", error);
      } finally {
        setIsLoaded(true);
      }
    };

    void loadLocale();
  }, []);

  const updateLocale = useCallback((nextLocale: SupportedLocale) => {
    if (i18n.locale !== nextLocale) {
      i18n.locale = nextLocale;
    }
    setLocaleState(nextLocale);
  }, []);

  const setLocale = useCallback(
    async (nextLocale: SupportedLocale) => {
      if (!SUPPORTED_LOCALES.includes(nextLocale)) {
        console.warn(`Unsupported locale: ${nextLocale}`);
        return;
      }

      try {
        updateLocale(nextLocale);
        await setStorageItem(LOCALE_STORAGE_KEY, nextLocale);
      } catch (error) {
        console.error("Error setting locale:", error);
      }
    },
    [updateLocale]
  );

  const translate = useCallback(
    (...args: Parameters<typeof i18n.t>) => i18n.t(...args),
    [locale]
  );

  const value = useMemo<I18nContextValue>(
    () => ({
      t: translate,
      locale,
      setLocale,
      isLoaded,
    }),
    [locale, setLocale, translate, isLoaded]
  );

  return createElement(I18nContext.Provider, { value }, children);
};

export const useI18n = () => {
  const context = useContext(I18nContext);
  if (!context) {
    throw new Error("useI18n must be used within an I18nProvider");
  }

  return context;
};
