"use client";

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
} from "react";
import { I18nextProvider } from "react-i18next";
import { createI18nInstance } from "@/lib/i18n/createI18nInstance";
import {
  getDirection,
  type AppLanguage,
} from "@/lib/i18n/registry";
import {
  persistLanguagePreference,
  readStoredLanguagePreference,
} from "@/lib/i18n/clientLanguage";
import { syncDocumentLanguage } from "@/lib/i18n/syncDocumentLanguage";

type LanguageContextValue = {
  language: AppLanguage;
  dir: "rtl" | "ltr";
  isRtl: boolean;
  ready: boolean;
  setLanguage: (lang: AppLanguage) => Promise<void>;
};

const LanguageContext = createContext<LanguageContextValue | null>(null);

export function LanguageProvider({
  children,
  initialLanguage,
}: {
  children: React.ReactNode;
  initialLanguage: AppLanguage;
}) {
  // One i18next instance per render of this component — since Next.js renders
  // "use client" components fresh on the server for each request (not from a
  // shared module-level singleton), this naturally gives every request its own
  // isolated instance instead of sharing one across the whole server process.
  // That's what actually fixes the hydration mismatch: the old shared singleton
  // was stuck on the server's default language for the life of the process,
  // so every visitor's page content server-rendered in that language and then
  // flipped to the real one after hydration.
  const [instance] = useState(() => createI18nInstance(initialLanguage));

  // initialLanguage is resolved server-side (cookie → Accept-Language → default)
  // and passed down as a prop, so server and first client render agree from the
  // start — no more guessing a different value on the client than what the
  // server already committed to in the HTML.
  const [language, setLanguageState] = useState<AppLanguage>(initialLanguage);
  const [ready, setReady] = useState(true);

  useEffect(() => {
    // A stored preference (from an earlier explicit choice) can still disagree
    // with initialLanguage — e.g. localStorage survived a cleared cookie. Only
    // switch in that case; otherwise there's nothing to reconcile since the
    // server already rendered the right language.
    const stored = readStoredLanguagePreference();
    if (stored && stored !== initialLanguage) {
      void instance.changeLanguage(stored).then(() => {
        setLanguageState(stored);
        syncDocumentLanguage(stored);
      });
    } else if (!stored) {
      persistLanguagePreference(initialLanguage);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const setLanguage = useCallback(
    async (lang: AppLanguage) => {
      await instance.changeLanguage(lang);
      persistLanguagePreference(lang);
      setLanguageState(lang);
      syncDocumentLanguage(lang);
    },
    [instance]
  );

  const value = useMemo<LanguageContextValue>(
    () => ({
      language,
      dir: getDirection(language),
      isRtl: getDirection(language) === "rtl",
      ready,
      setLanguage,
    }),
    [language, ready, setLanguage]
  );

  return (
    <I18nextProvider i18n={instance}>
      <LanguageContext.Provider value={value}>{children}</LanguageContext.Provider>
    </I18nextProvider>
  );
}

export function useAppLanguage() {
  const ctx = useContext(LanguageContext);
  if (!ctx) {
    throw new Error("useAppLanguage must be used within LanguageProvider");
  }
  return ctx;
}
