/* 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/. */

/* import-globals-from preferences.js */

ChromeUtils.defineESModuleGetters(this, {
  ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
  FirefoxLabs: "resource://nimbus/FirefoxLabs.sys.mjs",
});

const gExperimentalPane = {
  inited: false,
  _featureGatesContainer: null,
  _firefoxLabs: null,

  async init() {
    if (this.inited) {
      return;
    }

    this.inited = true;
    this._featureGatesContainer = document.getElementById(
      "pane-experimental-featureGates"
    );

    this._onCheckboxChanged = this._onCheckboxChanged.bind(this);
    this._onNimbusUpdate = this._onNimbusUpdate.bind(this);
    this._onStudiesEnabledChanged = this._onStudiesEnabledChanged.bind(this);
    this._resetAllFeatures = this._resetAllFeatures.bind(this);

    setEventListener(
      "experimentalCategory-reset",
      "click",
      this._resetAllFeatures
    );

    Services.obs.addObserver(
      this._onStudiesEnabledChanged,
      ExperimentAPI.STUDIES_ENABLED_CHANGED
    );
    window.addEventListener("unload", () => this._removeObservers());

    await this._maybeRenderLabsRecipes();
  },

  async _maybeRenderLabsRecipes() {
    this._firefoxLabs = await FirefoxLabs.create();

    const shouldHide = this._firefoxLabs.count === 0;
    this._setCategoryVisibility(shouldHide);

    if (shouldHide) {
      return;
    }

    const frag = document.createDocumentFragment();

    const groups = new Map();
    for (const optIn of this._firefoxLabs.all()) {
      if (!groups.has(optIn.firefoxLabsGroup)) {
        groups.set(optIn.firefoxLabsGroup, []);
      }

      groups.get(optIn.firefoxLabsGroup).push(optIn);
    }

    for (const [group, optIns] of groups) {
      const card = document.createElement("moz-card");
      card.classList.add("featureGate");

      const fieldset = document.createElement("moz-fieldset");
      document.l10n.setAttributes(fieldset, group);

      card.append(fieldset);

      for (const optIn of optIns) {
        const checkbox = document.createElement("moz-checkbox");
        checkbox.dataset.nimbusSlug = optIn.slug;
        checkbox.dataset.nimbusBranchSlug = optIn.branches[0].slug;
        const description = document.createElement("div");
        description.slot = "description";
        description.id = `${optIn.slug}-description`;
        description.classList.add("featureGateDescription");

        for (const [key, value] of Object.entries(
          optIn.firefoxLabsDescriptionLinks ?? {}
        )) {
          const link = document.createElement("a");
          link.setAttribute("data-l10n-name", key);
          link.setAttribute("href", value);
          link.setAttribute("target", "_blank");

          description.append(link);
        }

        document.l10n.setAttributes(description, optIn.firefoxLabsDescription);
        checkbox.id = optIn.slug;
        checkbox.setAttribute("aria-describedby", description.id);
        document.l10n.setAttributes(checkbox, optIn.firefoxLabsTitle);

        checkbox.checked =
          ExperimentAPI.manager.store.get(optIn.slug)?.active ?? false;
        checkbox.addEventListener("change", this._onCheckboxChanged);

        checkbox.append(description);
        fieldset.append(checkbox);
      }

      frag.append(card);
    }

    this._featureGatesContainer.appendChild(frag);

    ExperimentAPI.manager.store.on("update", this._onNimbusUpdate);

    Services.obs.notifyObservers(window, "experimental-pane-loaded");
  },

  _removeLabsRecipes() {
    ExperimentAPI.manager.store.off("update", this._onNimbusUpdate);

    this._featureGatesContainer
      .querySelectorAll(".featureGate")
      .forEach(el => el.remove());
  },

  async _onCheckboxChanged(event) {
    const target = event.target;

    const slug = target.dataset.nimbusSlug;
    const branchSlug = target.dataset.nimbusBranchSlug;

    const enrolling = !(ExperimentAPI.manager.store.get(slug)?.active ?? false);

    let shouldRestart = false;
    if (this._firefoxLabs.get(slug).requiresRestart) {
      const buttonIndex = await confirmRestartPrompt(enrolling, 1, true, false);
      shouldRestart = buttonIndex === CONFIRM_RESTART_PROMPT_RESTART_NOW;

      if (!shouldRestart) {
        // The user declined to restart, so we will not enroll in the opt-in.
        target.checked = false;
        return;
      }
    }

    // Disable the checkbox so that the user cannot interact with it during enrollment.
    target.disabled = true;

    if (enrolling) {
      await this._firefoxLabs.enroll(slug, branchSlug);
    } else {
      this._firefoxLabs.unenroll(slug);
    }

    target.disabled = false;

    if (shouldRestart) {
      Services.startup.quit(
        Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart
      );
    }
  },

  _onNimbusUpdate(_event, { slug, active }) {
    if (this._firefoxLabs.get(slug)) {
      document.getElementById(slug).checked = active;
    }
  },

  async _onStudiesEnabledChanged() {
    const studiesEnabled = ExperimentAPI.studiesEnabled;

    if (studiesEnabled) {
      await this._maybeRenderLabsRecipes();
    } else {
      this._setCategoryVisibility(true);
      this._removeLabsRecipes();
      this._firefoxLabs = null;
    }
  },

  _removeObservers() {
    ExperimentAPI.manager.store.off("update", this._onNimbusUpdate);
    Services.obs.removeObserver(
      this._onStudiesEnabledChanged,
      ExperimentAPI.STUDIES_ENABLED_CHANGED
    );
  },

  // Reset the features to their default values
  async _resetAllFeatures() {
    for (const optIn of this._firefoxLabs.all()) {
      const enrolled =
        (await ExperimentAPI.manager.store.get(optIn.slug)?.active) ?? false;
      if (enrolled) {
        this._firefoxLabs.unenroll(optIn.slug);
      }
    }
  },

  _setCategoryVisibility(shouldHide) {
    document.getElementById("category-experimental").hidden = shouldHide;

    // Cache the visibility so we can show it quicker in subsequent loads.
    Services.prefs.setBoolPref(
      "browser.preferences.experimental.hidden",
      shouldHide
    );

    if (
      shouldHide &&
      document.getElementById("categories").selectedItem?.id ==
        "category-experimental"
    ) {
      // Leave the 'experimental' category if there are no available features
      gotoPref("general");
    }
  },
};
