"use client";

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
  type ReactNode,
} from "react";
import { en, type Dictionary } from "./dictionaries/en";
import { ar } from "./dictionaries/ar";
import { DEFAULT_LOCALE, dirFor, isLocale, STORAGE_KEY, type I18nValue, type Locale } from "./types";

/**
 * A typed, tiny i18n provider. No external dependency on purpose: the app needs
 * exactly two locales and a script in the root layout already applies the saved
 * one before the first paint to avoid an RTL/LTR flash.
 *
 * `t` interpolates `{name}` segments. Values are always strings, so translators
 * cannot accidentally return objects and RTL keying stays predictable.
 */
const I18nContext = createContext<I18nValue | null>(null);

const dictionaries: Record<Locale, Dictionary> = { en, ar };

function interpolate(template: string, variables?: Record<string, string | number>): string {
  if (!variables) return template;
  return template.replace(/\{(\w+)\}/g, (match, key: string) =>
    key in variables ? String(variables[key]) : match,
  );
}

export function I18nProvider({ children }: { children: ReactNode }) {
  const [locale, setLocaleState] = useState<Locale>(DEFAULT_LOCALE);

  useEffect(() => {
    const raw = localStorage.getItem(STORAGE_KEY);
    const saved = isLocale(raw) ? raw : null;
    // Read the saved locale after mount on purpose: SSR is always "en", so a
    // lazy initializer would break hydration; the inline script already set
    // <html lang/dir> before first paint.
    // eslint-disable-next-line react-hooks/set-state-in-effect
    if (saved) setLocaleState(saved);
  }, []);

  const setLocale = useCallback((next: Locale) => {
    try {
      localStorage.setItem(STORAGE_KEY, next);
    } catch {
      // private mode — the session still carries it
    }
    setLocaleState(next);
    document.documentElement.lang = next;
    document.documentElement.dir = dirFor(next);
  }, []);

  const toggle = useCallback(() => {
    setLocaleState((current) => {
      const next: Locale = current === "en" ? "ar" : "en";
      try {
        localStorage.setItem(STORAGE_KEY, next);
      } catch {
        // ignore
      }
      document.documentElement.lang = next;
      document.documentElement.dir = dirFor(next);
      return next;
    });
  }, []);

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

  const value = useMemo<I18nValue>(
    () => ({ locale, dir: dirFor(locale), setLocale, toggle }),
    [locale, setLocale, toggle],
  );

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

export function useI18n() {
  const context = useContext(I18nContext);
  if (!context) throw new Error("useI18n must be used inside <I18nProvider>");
  return context;
}

/** The `t` helper with the current locale baked in. */
export function useT() {
  const { locale } = useI18n();
  return useCallback(
    (key: keyof Dictionary, variables?: Record<string, string | number>) =>
      interpolate(dictionaries[locale][key], variables),
    [locale],
  );
}

export type { Locale, I18nValue } from "./types";
export type { Dictionary } from "./dictionaries/en";