"use strict";

const SearchWidget = {
  _initialized: false,
  _initialOnionize: false,

  /**
   * Initialize the search form elements.
   */
  init() {
    this._initialized = true;

    this.searchForm = document.getElementById("search-form");
    this.onionizeToggle = document.getElementById("onionize-toggle");
    this.onionizeToggle.pressed = this._initialOnionize;
    this._updateOnionize();
    this.onionizeToggle.addEventListener("toggle", () =>
      this._updateOnionize()
    );

    // If the user submits, save the onionize search state for the next about:tor
    // page.
    this.searchForm.addEventListener("submit", () => {
      dispatchEvent(
        new CustomEvent("SubmitSearchOnionize", {
          detail: this.onionizeToggle.pressed,
          bubbles: true,
        })
      );
    });

    // By default, Enter on the onionizeToggle will toggle the button rather
    // than submit the <form>.
    // Moreover, our <form> has no submit button, so can only be submitted by
    // pressing Enter.
    // For keyboard users, Space will also toggle the form. We do not want to
    // require users to have to Tab back to the search input in order to press
    // Enter to submit the form.
    // For mouse users, clicking the toggle button will give it focus, so they
    // would have to Tab back or click the search input in order to submit the
    // form.
    // So we want to intercept the Enter keydown event to submit the form.
    this.onionizeToggle.addEventListener(
      "keydown",
      event => {
        if (event.key !== "Enter") {
          return;
        }
        event.preventDefault();
        event.stopPropagation();
        this.searchForm.requestSubmit();
      },
      { capture: true }
    );
  },

  _updateOnionize() {
    // Change submit URL based on the onionize toggle.
    this.searchForm.action = this.onionizeToggle.pressed
      ? "https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion"
      : "https://duckduckgo.com";
    this.searchForm.classList.toggle(
      "onionized-search",
      this.onionizeToggle.pressed
    );
  },

  /**
   * Set what the "Onionize" toggle state.
   *
   * @param {boolean} state - Whether the "Onionize" toggle should be switched
   *   on.
   */
  setOnionizeState(state) {
    if (!this._initialized) {
      this._initialOnionize = state;
      return;
    }
    this.onionizeToggle.pressed = state;
    this._updateOnionize();
  },
};

const MessageArea = {
  _initialized: false,
  _messageData: null,
  _isStable: null,
  _torConnectEnabled: null,

  /**
   * Initialize the message area and heading once elements are available.
   */
  init() {
    this._initialized = true;
    this._update();
  },

  /**
   * Set the message data and stable release flag.
   *
   * @param {MessageData} messageData - The message data, indicating which
   *   message to show.
   * @param {boolean} isStable - Whether this is the stable release version.
   * @param {boolean} torConnectEnabled - Whether TorConnect is enabled, and
   *   therefore the Tor process was configured with about:torconnect.
   */
  setMessageData(messageData, isStable, torConnectEnabled) {
    this._messageData = messageData;
    this._isStable = isStable;
    this._torConnectEnabled = torConnectEnabled;
    this._update();
  },

  _update() {
    if (!this._initialized) {
      return;
    }

    document
      .querySelector(".home-message.shown-message")
      ?.classList.remove("shown-message");

    if (!this._messageData) {
      return;
    }

    // Set heading.
    document.body.classList.toggle("is-testing", !this._isStable);

    document.body.classList.toggle("show-tor-check", !this._torConnectEnabled);

    const { updateVersion, updateURL, number } = this._messageData;

    if (updateVersion) {
      const updatedElement = document.getElementById("home-message-updated");
      updatedElement.querySelector("a").href = updateURL;
      document.l10n.setAttributes(
        updatedElement.querySelector("span"),
        "tor-browser-home-message-updated",
        { version: updateVersion }
      );
      updatedElement.classList.add("shown-message");
    } else {
      const messageElements = document.querySelectorAll(
        this._isStable
          ? ".home-message-rotating-stable"
          : ".home-message-rotating-testing"
      );
      messageElements[number % messageElements.length].classList.add(
        "shown-message"
      );
    }
  },
};

/**
 * A reusable area for surveys.
 *
 * Initially used for tor-browser#43504.
 */
const SurveyArea = {
  /**
   * The current version of the survey.
   *
   * Should be increased every time we start a new survey campaign.
   *
   * @type {integer}
   */
  _version: 1,

  /**
   * The date to start showing the survey.
   *
   * @type {?integer}
   */
  _startDate: null, // No survey date.

  /**
   * The date to stop showing the current survey.
   *
   * @type {?integer}
   */
  _endDate: null, // No survey date.

  /**
   * The survey URL.
   *
   * @type {string}
   */
  _urlBase: "https://survey.torproject.org/index.php/923269",

  /**
   * @typedef {object} SurveyLocaleData
   *
   * Locale-specific data for the survey.
   *
   * @property {string[]} browserLocales - The browser locales this should match
   *   with. The first locale should match the locale of the strings.
   * @property {string} urlCode - The language code to pass to the survey URL.
   * @property {string} dir - The direction of the locale.
   * @property {object} strings - The strings to use for the survey banner.
   */

  /**
   * The data for the selected locale.
   *
   * @type {SurveyLocaleData}
   */
  _localeData: null,

  /**
   * The data for each locale that is supported.
   *
   * The first entry is the default.
   *
   * @type {SurveyLocaleData[]}
   */
  _localeDataSet: [
    {
      browserLocales: ["en-US"],
      dir: "ltr",
      urlCode: "en",
      strings: {
        heading: "We’d love your feedback",
        body: "Help us improve Tor Browser by completing this 10-minute survey.",
        launch: "Launch the survey",
        dismiss: "Dismiss",
        close: "Close",
      },
    },
    {
      browserLocales: ["es-ES"],
      dir: "ltr",
      urlCode: "es",
      strings: {
        heading: "Danos tu opinión",
        body: "Ayúdanos a mejorar el Navegador Tor completando esta encuesta de 10 minutos.",
        launch: "Iniciar la encuesta",
        dismiss: "Descartar",
        close: "Cerrar",
      },
    },
    {
      browserLocales: ["ru"],
      dir: "ltr",
      urlCode: "ru",
      strings: {
        heading: "Мы будем рады вашим отзывам",
        body: "Помогите нам улучшить браузер Tor, пройдя 10-минутный опрос.",
        launch: "Начать опрос",
        dismiss: "Отклонить",
        close: "Закрыть",
      },
    },
    {
      browserLocales: ["fr"],
      dir: "ltr",
      urlCode: "fr",
      strings: {
        heading: "Nous serions ravis d’avoir votre avis !",
        body: "Aidez-nous à améliorer le navigateur Tor en répondant à cette enquête de 10 minutes.",
        launch: "Lancer l'enquête",
        dismiss: "Ignorer",
        close: "Fermer",
      },
    },
    {
      // Also show this pt-BR banner for the pt-PT browser locale.
      browserLocales: ["pt-BR", "pt-PT"],
      dir: "ltr",
      urlCode: "pt-BR",
      strings: {
        heading: "Adoraríamos ouvir sua opinião",
        body: "Ajude-nos a melhorar o Navegador Tor respondendo a esta pesquisa de 10 minutos.",
        launch: "Iniciar a pesquisa",
        dismiss: "Dispensar",
        close: "Fechar",
      },
    },
  ],

  /**
   * Whether the area has been initialised.
   *
   * @type {boolean}
   */
  _initialized: false,

  /**
   * The app locale, or `null` whilst unset.
   *
   * @type {?string}
   */
  _locale: null,

  /**
   * Whether the banner should be shown.
   *
   * @type {boolean}
   */
  _shouldShow: false,

  /**
   * The survey element.
   *
   * @type {?Element}
   */
  _areaEl: null,

  /**
   * Initialize the survey area.
   */
  init() {
    this._initialized = true;

    this._areaEl = document.getElementById("survey");
    document.getElementById("survey-launch").addEventListener("click", () => {
      const url = URL.parse(this._urlBase);
      if (!url || !this._localeData) {
        return;
      }

      url.searchParams.append("lang", this._localeData.urlCode);
      open(url.href);
    });
    document.getElementById("survey-close").addEventListener("click", () => {
      this._hide();
    });
    document.getElementById("survey-dismiss").addEventListener("click", () => {
      this._hide();
    });

    this._update();
  },

  /**
   * Permanently hide this survey.
   */
  _hide() {
    this._shouldShow = false;
    this._update();

    dispatchEvent(
      new CustomEvent("SurveyDismissed", {
        // We pass in the current survey version to record the *latest*
        // version that the user has dismissed. This will overwrite any
        // previous versions.
        detail: this._version,
        bubbles: true,
      })
    );
  },

  /**
   * Decide whether to show the survey.
   *
   * @param {integer} dismissVersion - The latest version of survey that the
   *   user has already dismissed.
   * @param {boolean} isStable - Whether this is the stable release of Tor
   *   Browser.
   * @param {string} appLocale - The app locale currently in use.
   */
  potentiallyShow(dismissVersion, isStable, appLocale) {
    const now = Date.now();
    this._shouldShow =
      isStable &&
      dismissVersion < this._version &&
      this._startDate &&
      now >= this._startDate &&
      now < this._endDate;
    this._locale = appLocale;
    this._update();
  },

  /**
   * Update the display.
   */
  _update() {
    if (!this._initialized) {
      return;
    }
    if (!this._shouldShow) {
      if (this._areaEl.contains(document.activeElement)) {
        // Move focus to the search input.
        document.getElementById("search-input").focus();
      }
      document.body.classList.remove("show-survey");
      return;
    }

    // Determine the survey locale based on the app locale.
    // NOTE: We do not user document.l10n to translate the survey banner.
    // Instead we only translate the banner into a limited set of locales that
    // match the languages that the survey itself supports. This should match
    // the language of the survey when it is opened by the user.
    for (const localeData of this._localeDataSet) {
      if (localeData.browserLocales.includes(this._locale)) {
        this._localeData = localeData;
        break;
      }
    }
    if (!this._localeData) {
      // Show the default en-US banner.
      this._localeData = this._localeDataSet[0];
    }

    // Make sure the survey's lang and dir attributes match the chosen locale.
    const surveyEl = document.getElementById("survey");
    surveyEl.setAttribute("lang", this._localeData.browserLocales[0]);
    surveyEl.setAttribute("dir", this._localeData.dir);

    const { heading, body, launch, dismiss, close } = this._localeData.strings;

    document.getElementById("survey-heading").textContent = heading;
    document.getElementById("survey-body").textContent = body;
    document.getElementById("survey-launch").textContent = launch;
    document.getElementById("survey-dismiss").textContent = dismiss;
    document.getElementById("survey-close").setAttribute("title", close);

    document.body.classList.add("show-survey");
  },
};

/**
 * Area for the Year End Campaign (YEC).
 * See tor-browser#42072.
 */
const YecArea = {
  /**
   * The epoch time to start showing the banner, if at all.
   *
   * @type {?integer}
   */
  _startDate: Date.UTC(2026, 4, 19, 15), // 19th May 2026, 15 UTC.

  /**
   * The epoch time to stop showing the banner, if at all.
   *
   * @type {?integer}
   */
  _endDate: Date.UTC(2026, 5, 19, 0), // 19th June 2026, 0 UTC.

  /**
   * Whether the area has been initialised.
   *
   * @type {boolean}
   */
  _initialized: false,

  /**
   * The app locale, or `null` whilst unset.
   *
   * @type {?string}
   */
  _locale: null,

  /**
   * Whether the banner should be shown.
   *
   * @type {boolean}
   */
  _shouldShow: false,

  /**
   * The tool number to show first, or `null` whilst unset.
   *
   * @type {?number}
   */
  _toolNum1: null,

  /**
   * The tool number to show second, or `null` whilst unset.
   *
   * @type {?number}
   */
  _toolNum2: null,

  /**
   * The name and description data for each tool.
   *
   * @type {object[]}
   */
  _toolData: [
    {
      name: "Onion Browser",
      descId: "summer-2026-funding-tool-onion-browser-description",
    },
    {
      name: "Quiet",
      descId: "summer-2026-funding-tool-quiet-description",
    },
    {
      name: "Ricochet Refresh",
      descId: "summer-2026-funding-tool-ricochet-refresh-description",
    },
    {
      name: "SecureDrop",
      descId: "summer-2026-funding-tool-securedrop-description",
    },
    {
      name: "OnionShare",
      descId: "summer-2026-funding-tool-onionshare-description",
    },
    {
      name: "Digital Security Helpdesk",
      descId: "summer-2026-funding-tool-digital-security-helpdesk-description",
    },
    {
      name: "Paskoocheh",
      descId: "summer-2026-funding-tool-paskoocheh-description",
    },
    {
      name: "Unredacted",
      descId: "summer-2026-funding-tool-unredacted-description",
    },
    {
      name: "Osservatorio Nessuno",
      descId: "summer-2026-funding-tool-osservatorio-nessuno-description",
    },
    {
      name: "Save",
      descId: "summer-2026-funding-tool-save-description",
    },
    {
      name: "OONI",
      descId: "summer-2026-funding-tool-ooni-description",
    },
  ],

  /**
   * The banner element.
   *
   * @type {?Element}
   */
  _areaEl: null,

  /**
   * Initialize the widget.
   */
  init() {
    this._initialized = true;

    this._areaEl = document.getElementById("yec-banner");

    document.getElementById("yec-close").addEventListener("click", () => {
      this.dismiss();
      dispatchEvent(new CustomEvent("UserDismissedYEC", { bubbles: true }));
    });

    this._update();
  },

  /**
   * Close the banner.
   */
  dismiss() {
    this._shouldShow = false;
    this._update();
  },

  /**
   * Possibly show the banner.
   *
   * @param {boolean} dismissYEC - Whether the user has dismissed YEC.
   * @param {boolean} isStable - Whether this is a stable release.
   * @param {string} appLocale - The app locale, as BCP47.
   * @param {number} toolNum1 - The number for the first tool to show.
   * @param {number} toolNum2 - The number for the first tool to show.
   */
  potentiallyShow(dismissYEC, isStable, appLocale, toolNum1, toolNum2) {
    const now = Date.now();
    this._shouldShow =
      !dismissYEC &&
      isStable &&
      this._startDate &&
      now >= this._startDate &&
      now < this._endDate;
    this._locale = appLocale;
    this._toolNum1 = toolNum1;
    this._toolNum2 = toolNum2;
    this._update();
  },

  /**
   * Update the visibility of the banner to reflect the new state.
   */
  async _update() {
    if (!this._initialized) {
      return;
    }
    if (!this._shouldShow) {
      if (this._areaEl.contains(document.activeElement)) {
        document.documentElement.focus();
      }
      document.body.classList.remove("show-yec");
      return;
    }

    const tool1 = this._toolData[this._toolNum1];
    const tool2 = this._toolData[this._toolNum2];

    const [tool1Desc, tool2Desc] = await document.l10n.formatValues([
      { id: tool1.descId },
      { id: tool2.descId },
    ]);

    document.l10n.setAttributes(
      document.getElementById("yec-body"),
      "summer-2026-funding-intro",
      {
        "tool1-name": tool1.name,
        "tool1-description": tool1Desc,
        "tool2-name": tool2.name,
        "tool2-description": tool2Desc,
      }
    );

    const donateLink = document.getElementById("yec-donate-link");
    const base = "https://internetfreedom.torproject.org";
    donateLink.href = base;

    document.body.classList.add("show-yec");
  },
};

let gInitialData = false;
let gLoaded = false;

function maybeComplete() {
  if (!gInitialData || !gLoaded) {
    return;
  }
  // Wait to show the content when the l10n population has completed.
  if (document.hasPendingL10nMutations) {
    window.addEventListener(
      "L10nMutationsFinished",
      () => {
        document.body.classList.add("initialized");
      },
      { once: true }
    );
  } else {
    document.body.classList.add("initialized");
  }
}

window.addEventListener("DOMContentLoaded", () => {
  SearchWidget.init();
  MessageArea.init();
  SurveyArea.init();
  YecArea.init();

  gLoaded = true;
  maybeComplete();
});

window.addEventListener("InitialData", event => {
  const {
    torConnectEnabled,
    isStable,
    searchOnionize,
    messageData,
    surveyDismissVersion,
    appLocale,
    dismissYEC,
    toolNum1,
    toolNum2,
  } = event.detail;
  SearchWidget.setOnionizeState(!!searchOnionize);
  MessageArea.setMessageData(messageData, !!isStable, !!torConnectEnabled);
  SurveyArea.potentiallyShow(surveyDismissVersion, isStable, appLocale);
  YecArea.potentiallyShow(dismissYEC, isStable, appLocale, toolNum1, toolNum2);

  gInitialData = true;
  maybeComplete();
});

window.addEventListener("DismissYEC", () => {
  // User closed the banner in another about:tor instance.
  YecArea.dismiss();
});
