// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.

import {
  type BackendModule,
  type InitOptions,
  default as i18n,
  type LanguageDetectorModule,
  type ReadCallback,
  type ResourceKey,
} from "i18next";
import { initReactI18next } from "react-i18next";

// This generates a map of locale names to their URL (based on import.meta.url), which looks like this:
// {
//   "../locales/en.json": "/whatever/assets/root/locales/en-aabbcc.json",
//   ...
// }
const locales = import.meta.glob<string>("../locales/*.json", {
  query: "?url",
  import: "default",
  eager: true,
});

const getLocaleUrl = (name: string): string | undefined =>
  locales[`../locales/${name}.json`];

const supportedLngs = Object.keys(locales).map((url) => {
  const lang = url.match(/\/([^/]+)\.json$/)?.[1];
  if (!lang) {
    throw new Error(`Could not parse locale URL ${url}`);
  }
  return lang;
});

// A simple language detector that reads the `lang` attribute from the HTML tag
const LanguageDetector = {
  type: "languageDetector",

  detect(): string | undefined {
    const htmlTag =
      typeof document !== "undefined" ? document.documentElement : null;

    if (htmlTag && typeof htmlTag.getAttribute === "function") {
      return htmlTag.getAttribute("lang") || undefined;
    }
  },
} satisfies LanguageDetectorModule;

// A backend that fetches the locale files from the URLs generated by the glob above
const Backend = {
  type: "backend",
  init(): void {},
  read(language: string, _namespace: string, callback: ReadCallback): void {
    (async (): Promise<ResourceKey> => {
      const url = getLocaleUrl(language);
      if (!url) {
        throw new Error(`Locale ${language} not found`);
      }

      const response = await fetch(url, {
        credentials: "omit",
        headers: {
          Accept: "application/json",
        },
      });

      if (!response.ok) {
        throw Error(`Failed to fetch ${url}`);
      }

      // XXX: we don't check the JSON shape here, which should be fine
      return await response.json();
    })().then(
      (data) => callback(null, data),
      (error) => callback(error, null),
    );
  },
} satisfies BackendModule;

export const setupI18n = () => {
  i18n
    .use(Backend)
    .use(LanguageDetector)
    .use(initReactI18next)
    .init({
      fallbackLng: "en",
      keySeparator: ".",
      pluralSeparator: ":",
      defaultNS: "translation",
      supportedLngs,
      interpolation: {
        escapeValue: false, // React has built-in XSS protections
      },
    } satisfies InitOptions);
};

import.meta.hot?.on("locales-update", () => {
  i18n.reloadResources().then(() => {
    i18n.changeLanguage(i18n.language);
  });
});

export default i18n;
