"use client";

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

import enMessages from "./i18n/messages/en";

export type Locale =
  | "en"
  | "fr"
  | "es"
  | "de"
  | "pt"
  | "it"
  | "nl"
  | "ru"
  | "ja"
  | "ko"
  | "zh"
  | "ar"
  | "fa";

const VALID_LOCALES: ReadonlyArray<Locale> = [
  "en",
  "fr",
  "es",
  "de",
  "pt",
  "it",
  "nl",
  "ru",
  "ja",
  "ko",
  "zh",
  "ar",
  "fa",
];

const DEFAULT_LOCALE: Locale = "en";
// Locales d'affichage écrites de droite à gauche. Doit couvrir toutes les
// locales RTL de VALID_LOCALES — la référence côté serveur est
// RTL_LANGUAGES dans core/languages.py (ar, he, fa, ur, ku, ps, ug, sd,
// yi, dv, ckb) : toute nouvelle locale RTL ajoutée à VALID_LOCALES doit
// figurer ici, sinon l'interface restera affichée de gauche à droite.
const RTL_LOCALES: ReadonlyArray<Locale> = ["ar", "fa"];

type TranslationParams = Record<string, string | number>;

interface I18nContextValue {
  locale: Locale;
  dir: "ltr" | "rtl";
  isRTL: boolean;
  setLocale: (locale: Locale) => void;
  t: (key: string, params?: TranslationParams) => string;
  isLoading: boolean;
}

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

const messageCache: Partial<Record<Locale, Record<string, string>>> = {
  en: enMessages,
};

function interpolate(template: string, params?: TranslationParams): string {
  if (!params) return template;
  return Object.entries(params).reduce(
    (acc, [k, v]) => acc.replace(new RegExp(`\\{${k}\\}`, "g"), String(v)),
    template,
  );
}

function detectInitialLocale(): Locale {
  if (typeof window === "undefined") return DEFAULT_LOCALE;
  const saved = localStorage.getItem("locale");
  if (saved && (VALID_LOCALES as readonly string[]).includes(saved)) {
    return saved as Locale;
  }
  const primary = navigator.language.split("-")[0];
  if ((VALID_LOCALES as readonly string[]).includes(primary)) {
    return primary as Locale;
  }
  const allLangs = navigator.languages || [];
  for (const lang of allLangs) {
    const code = lang.split("-")[0];
    if ((VALID_LOCALES as readonly string[]).includes(code)) {
      return code as Locale;
    }
  }
  return DEFAULT_LOCALE;
}

async function loadLocaleMessages(locale: Locale): Promise<Record<string, string>> {
  if (messageCache[locale]) return messageCache[locale];

  try {
    const mod = await import(`./i18n/messages/${locale}`);
    messageCache[locale] = mod.default as Record<string, string>;
    return messageCache[locale];
  } catch (err) {
    console.warn(`Failed to load locale messages for ${locale}`, err);
    messageCache[locale] = enMessages;
    return enMessages;
  }
}

export function formatDate(
  date: Date,
  locale: Locale,
  options?: Intl.DateTimeFormatOptions,
): string {
  const calendar =
    locale === "fa" ? "fa-IR-u-ca-persian" : locale === "ar" ? "ar-SA" : locale;
  const defaults: Intl.DateTimeFormatOptions = {
    day: "numeric",
    month: "long",
    year: "numeric",
  };
  return date.toLocaleDateString(calendar, { ...defaults, ...options });
}

export function I18nProvider({ children }: { children: ReactNode }) {
  const [locale, setLocaleState] = useState<Locale>(detectInitialLocale);
  const [messages, setMessages] = useState<Record<string, string>>(enMessages);
  const [isLoading, setIsLoading] = useState(false);

  const isRTL = (RTL_LOCALES as readonly string[]).includes(locale);
  const dir = isRTL ? ("rtl" as const) : ("ltr" as const);

  useEffect(() => {
    document.documentElement.dir = dir;
    document.documentElement.lang = locale;
  }, [locale, dir]);

  useEffect(() => {
    let cancelled = false;
    if (locale === "en") {
      setMessages(enMessages);
      return;
    }
    setIsLoading(true);
    loadLocaleMessages(locale).then((loaded) => {
      if (!cancelled) {
        setMessages(loaded);
        setIsLoading(false);
      }
    });
    return () => {
      cancelled = true;
    };
  }, [locale]);

  const setLocale = useCallback((newLocale: Locale) => {
    setLocaleState(newLocale);
    localStorage.setItem("locale", newLocale);
  }, []);

  const t = useCallback(
    (key: string, params?: TranslationParams): string => {
      const msg = messages[key] ?? enMessages[key];
      if (msg === undefined) {
        // Missing everywhere: honour an explicit defaultValue, else return ""
        // so `t(key) || fallback` call sites work and raw keys never reach the UI.
        return params && "defaultValue" in params
          ? String(params.defaultValue)
          : "";
      }
      const rest = { ...params };
      delete rest.defaultValue;
      return interpolate(msg, rest);
    },
    [messages],
  );

  const value = useMemo(
    () => ({ locale, dir, isRTL, setLocale, t, isLoading }),
    [locale, dir, isRTL, setLocale, t, isLoading],
  );

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

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

export function useTranslation() {
  const { t } = useI18n();
  return { t };
}
