/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * The downloaded Fluent file is located in this sub-directory of the local
 * profile directory.
 */
const USE_REMOTE_L10N_PREF =
  "browser.newtabpage.activity-stream.asrouter.useRemoteL10n";

/**
 * All supported locales for remote l10n
 *
 * This is used by ASRouter.sys.mjs to check if the locale is supported before
 * issuing the request for remote fluent files to RemoteSettings.
 *
 * Note:
 *   * this is generated based on "browser/locales/all-locales" as l10n doesn't
 *     provide an API to fetch that list
 *
 *   * this list doesn't include "en-US", though "en-US" is well supported and
 *     `_RemoteL10n.isLocaleSupported()` will handle it properly
 */
const ALL_LOCALES = new Set([
  "ach",
  "af",
  "an",
  "ar",
  "ast",
  "az",
  "be",
  "bg",
  "bn",
  "bo",
  "br",
  "brx",
  "bs",
  "ca",
  "ca-valencia",
  "cak",
  "ckb",
  "cs",
  "cy",
  "da",
  "de",
  "dsb",
  "el",
  "en-CA",
  "en-GB",
  "eo",
  "es-AR",
  "es-CL",
  "es-ES",
  "es-MX",
  "et",
  "eu",
  "fa",
  "ff",
  "fi",
  "fr",
  "fy-NL",
  "ga-IE",
  "gd",
  "gl",
  "gn",
  "gu-IN",
  "he",
  "hi-IN",
  "hr",
  "hsb",
  "hu",
  "hy-AM",
  "hye",
  "ia",
  "id",
  "is",
  "it",
  "ja",
  "ja-JP-mac",
  "ka",
  "kab",
  "kk",
  "km",
  "kn",
  "ko",
  "lij",
  "lo",
  "lt",
  "ltg",
  "lv",
  "meh",
  "mk",
  "mr",
  "ms",
  "my",
  "nb-NO",
  "ne-NP",
  "nl",
  "nn-NO",
  "oc",
  "pa-IN",
  "pl",
  "pt-BR",
  "pt-PT",
  "rm",
  "ro",
  "ru",
  "scn",
  "si",
  "sk",
  "sl",
  "son",
  "sq",
  "sr",
  "sv-SE",
  "szl",
  "ta",
  "te",
  "th",
  "tl",
  "tr",
  "trs",
  "uk",
  "ur",
  "uz",
  "vi",
  "wo",
  "xh",
  "zh-CN",
  "zh-TW",
]);

export class _RemoteL10n {
  constructor() {
    this._l10n = null;
  }

  createElement(doc, elem, options = {}) {
    let node;
    if (options.content && options.content.string_id) {
      node = doc.createElement("remote-text");
    } else {
      node = doc.createElementNS("http://www.w3.org/1999/xhtml", elem);
    }
    if (options.classList) {
      node.classList.add(options.classList);
    }
    this.setString(node, options);

    return node;
  }

  // If `string_id` is present it means we are relying on fluent for translations.
  // Otherwise, we have a vanilla string.
  // attributes: Fluent arguments to be used in substitutions in the string specified by the string_id
  setString(el, { content, attributes = {} }) {
    if (content && content.string_id) {
      for (let [fluentId, value] of Object.entries(attributes)) {
        el.setAttribute(`fluent-variable-${fluentId}`, value);
      }
      el.setAttribute("fluent-remote-id", content.string_id);
    } else {
      el.textContent = content;
    }
  }

  get cfrFluentFileDir() {
    return PathUtils.join(
      Services.dirsvc.get("ProfLD", Ci.nsIFile).path,
      "settings",
      "main",
      "ms-language-packs"
    );
  }

  get cfrFluentFilePath() {
    return PathUtils.join(
      this.cfrFluentFileDir,
      "browser",
      "newtab",
      "asrouter.ftl"
    );
  }

  /**
   * Creates a new DOMLocalization instance with the Fluent file from Remote Settings.
   *
   * Note: it will use the local Fluent file in any of following cases:
   *   * the remote Fluent file is not available
   *   * it was told to use the local Fluent file
   */
  _createDOML10n() {
    /* istanbul ignore next */
    let useRemoteL10n = Services.prefs.getBoolPref(USE_REMOTE_L10N_PREF, true);
    if (useRemoteL10n && !L10nRegistry.getInstance().hasSource("cfr")) {
      const appLocale = Services.locale.appLocaleAsBCP47;
      let cfrIndexedFileSource = new L10nFileSource(
        "cfr",
        "app",
        [appLocale],
        `${PathUtils.toFileURI(this.cfrFluentFileDir)}/`,
        {
          addResourceOptions: {
            allowOverrides: true,
          },
        },
        [PathUtils.toFileURI(this.cfrFluentFilePath)]
      );
      L10nRegistry.getInstance().registerSources([cfrIndexedFileSource]);
    } else if (!useRemoteL10n && L10nRegistry.getInstance().hasSource("cfr")) {
      L10nRegistry.getInstance().removeSources(["cfr"]);
    }

    return new DOMLocalization(
      [
        "branding/brand.ftl",
        "browser/defaultBrowserNotification.ftl",
        "browser/newtab/asrouter.ftl",
        "browser/policy-messages.ftl",
        "browser/profiles.ftl",
        "browser/termsofuse.ftl",
        "preview/aiWindow.ftl",
        "toolkit/branding/brandings.ftl",
      ],
      false
    );
  }

  get l10n() {
    if (!this._l10n) {
      this._l10n = this._createDOML10n();
    }
    return this._l10n;
  }

  reloadL10n() {
    this._l10n = null;
  }

  isLocaleSupported(locale) {
    return locale === "en-US" || ALL_LOCALES.has(locale);
  }

  /**
   * Format given `localizableText`.
   *
   * Format `localizableText` if it is an object using any `string_id` field,
   * otherwise return `localizableText` unmodified.
   *
   * @param {object|string} `localizableText` to format.
   * attributes: Fluent arguments to be used in substitutions in the string specified by the string_id
   * @return {string} formatted text.
   */
  async formatLocalizableText(localizableText, attributes) {
    if (typeof localizableText !== "string") {
      // It's more useful to get an error than passing through an object without
      // a `string_id` field.
      let value = await this.l10n.formatValue(
        localizableText.string_id,
        attributes
      );
      return value;
    }
    return localizableText;
  }
}

export const RemoteL10n = new _RemoteL10n();
