/* 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 {
  actionTypes as at,
  actionCreators as ac,
} from "resource://newtab/common/Actions.mjs";

const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
  BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs",
  HomePage: "resource:///modules/HomePage.sys.mjs",
});

const DEFAULT_HOMEPAGE_URL = "about:home";
const BLANK_HOMEPAGE_URL = "chrome://browser/content/blanktab.html";

/**
 * @backward-compat { version 150 }
 * `home-pane-loaded` is fired by home-startup.mjs (chrome, baked at build time).
 * This notification was introduced in Firefox 150, so the redesign path in
 * observe() cannot be train-hopped on earlier releases.
 */
export const PREFERENCES_LOADED_EVENT = "home-pane-loaded";

// These "section" objects are formatted in a way to be similar to the ones from
// SectionsManager to construct the preferences view.
const PREFS_FOR_SETTINGS = () => [
  {
    id: "web-search",
    pref: {
      feed: "showSearch",
      titleString: "home-prefs-search-header",
    },
  },
  {
    id: "weather",
    pref: {
      feed: "showWeather",
      titleString: "home-prefs-weather-header",
      descString: "home-prefs-weather-description",
      learnMore: {
        link: {
          href: "https://support.mozilla.org/kb/customize-items-on-firefox-new-tab-page",
          id: "home-prefs-weather-learn-more-link",
        },
      },
    },
    eventSource: "WEATHER",
    shouldHidePref: !Services.prefs.getBoolPref(
      "browser.newtabpage.activity-stream.system.showWeather",
      false
    ),
  },
  {
    id: "topsites",
    pref: {
      feed: "feeds.topsites",
      titleString: "home-prefs-shortcuts-header",
      descString: "home-prefs-shortcuts-description",
    },
    maxRows: 4,
    rowsPref: "topSitesRows",
    eventSource: "TOP_SITES",
  },
  {
    id: "topstories",
    pref: {
      feed: "feeds.section.topstories",
      titleString: {
        id: "home-prefs-recommended-by-header-generic",
      },
      descString: {
        id: "home-prefs-recommended-by-description-generic",
      },
    },
    shouldHidePref: !Services.prefs.getBoolPref(
      "browser.newtabpage.activity-stream.feeds.system.topstories",
      true
    ),
    eventSource: "TOP_STORIES",
  },
  {
    id: "support-firefox",
    pref: {
      feed: "showSponsoredCheckboxes",
      titleString: "home-prefs-support-firefox-header",
      nestedPrefs: [
        {
          name: "showSponsoredTopSites",
          titleString: "home-prefs-shortcuts-by-option-sponsored",
          eventSource: "SPONSORED_TOP_SITES",
        },
        {
          name: "showSponsored",
          titleString: "home-prefs-recommended-by-option-sponsored-stories",
          eventSource: "POCKET_SPOCS",
          shouldHidePref: !Services.prefs.getBoolPref(
            "browser.newtabpage.activity-stream.feeds.system.topstories",
            true
          ),
          shouldDisablePref: !Services.prefs.getBoolPref(
            "browser.newtabpage.activity-stream.feeds.section.topstories",
            true
          ),
        },
      ],
    },
  },
];

export class AboutPreferences {
  init() {
    Services.obs.addObserver(this, PREFERENCES_LOADED_EVENT);
  }

  uninit() {
    Services.obs.removeObserver(this, PREFERENCES_LOADED_EVENT);
  }

  onAction(action) {
    switch (action.type) {
      case at.INIT:
        this.init();
        break;
      case at.UNINIT:
        this.uninit();
        break;
      case at.SETTINGS_OPEN:
        action._target.browser.ownerGlobal.openPreferences("paneHome");
        break;
      // This is used to open the web extension settings page for an extension
      case at.OPEN_WEBEXT_SETTINGS:
        action._target.browser.ownerGlobal.BrowserAddonUI.openAddonsMgr(
          `addons://detail/${encodeURIComponent(action.data)}`
        );
        break;
    }
  }

  setupUserEvent(element, eventSource) {
    element.addEventListener("command", e => {
      const { checked } = e.target;
      if (typeof checked === "boolean") {
        this.store.dispatch(
          ac.UserEvent({
            event: "PREF_CHANGED",
            source: eventSource,
            value: { status: checked, menu_source: "ABOUT_PREFERENCES" },
          })
        );
      }
    });
  }

  observe(window) {
    if (Services.prefs.getBoolPref("browser.settings-redesign.enabled")) {
      const { SettingGroupManager } = window;

      /**
       * @backward-compat { version 150 }
       * On Firefox < 150, the preferences component was registering Home & New Tab groups
       * itself before firing `home-pane-loaded`. Skip re-registration on those versions.
       */
      if (!SettingGroupManager._data?.has("homepage")) {
        this._registerPreferences(window);

        SettingGroupManager.registerGroups({
          homepage: this._setupHomepageGroup(window),
          customHomepage: this._setupCustomHomepageGroup(window),
          home: this._setupHomeGroup(window),
        });
      }
      return;
    }

    // Legacy settings UI
    const { document, Preferences } = window;

    // Extract just the "Recent activity" pref info from SectionsManager as we have everything else already
    const highlights = this.store
      .getState()
      .Sections.find(el => el.id === "highlights");

    const allSections = [...PREFS_FOR_SETTINGS(), highlights];

    // Render the preferences
    allSections.forEach(pref => {
      this.renderPreferenceSection(pref, document, Preferences);
    });

    // Update the visibility of the Restore Defaults button based on checked prefs
    this.toggleRestoreDefaults(window.gHomePane);
  }

  /** @param {Window} window */
  _registerPreferences(window) {
    const { Preferences } = window;

    Preferences.addAll([
      { id: "browser.newtabpage.activity-stream.showSearch", type: "bool" },
      {
        id: "browser.newtabpage.activity-stream.system.showWeather",
        type: "bool",
      },
      { id: "browser.newtabpage.activity-stream.showWeather", type: "bool" },
      {
        id: "browser.newtabpage.activity-stream.widgets.system.enabled",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.widgets.enabled",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.widgets.system.lists.enabled",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.widgets.lists.enabled",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.widgets.system.focusTimer.enabled",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.widgets.focusTimer.enabled",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.feeds.topsites",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.topSitesRows",
        type: "int",
      },
      {
        id: "browser.newtabpage.activity-stream.feeds.system.topstories",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.feeds.section.topstories",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.discoverystream.sections.enabled",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.discoverystream.topicLabels.enabled",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.discoverystream.sections.personalization.enabled",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.discoverystream.sections.customizeMenuPanel.enabled",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.showSponsoredCheckboxes",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.showSponsoredTopSites",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.showSponsored",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.feeds.section.highlights",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.section.highlights.rows",
        type: "int",
      },
      {
        id: "browser.newtabpage.activity-stream.section.highlights.includeVisited",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.section.highlights.includeBookmarks",
        type: "bool",
      },
      {
        id: "browser.newtabpage.activity-stream.section.highlights.includeDownloads",
        type: "bool",
      },
    ]);
  }

  /** @param {Window} window */
  _setupHomepageGroup(window) {
    const { Preferences } = window;

    // Set up `browser.startup.homepage` to show homepage options for Homepage / New Windows
    Preferences.addSetting(
      /** @type {{ useCustomHomepage: boolean } & SettingConfig } */ ({
        id: "homepageNewWindows",
        pref: "browser.startup.homepage",
        useCustomHomepage: false,
        get(prefVal) {
          if (this.useCustomHomepage) {
            return "custom";
          }
          switch (prefVal) {
            case DEFAULT_HOMEPAGE_URL:
              return "home";
            case BLANK_HOMEPAGE_URL:
              return "blank";
            // Custom value can be any string so leaving it as default value to catch
            // non-default/blank entries.
            default:
              return "custom";
          }
        },
        set(inputVal, _, setting) {
          let wasCustomHomepage = this.useCustomHomepage;
          this.useCustomHomepage = inputVal === "custom";
          if (wasCustomHomepage !== this.useCustomHomepage) {
            setting.onChange();
          }
          switch (inputVal) {
            case "home":
              return DEFAULT_HOMEPAGE_URL;
            case "blank":
              return BLANK_HOMEPAGE_URL;
            case "custom":
              return setting.pref.value;
            default:
              throw new Error("No handler for this value");
          }
        },
      })
    );

    // Set up `browser.startup.homepage` again to update and display its value
    // on the Homepage and Custom Homepage settings panes.
    Preferences.addSetting({
      id: "homepageDisplayPref",
      pref: "browser.startup.homepage",
    });

    Preferences.addSetting({
      id: "disableCurrentPagesButton",
      pref: "pref.browser.homepage.disable_button.current_page",
    });

    Preferences.addSetting({
      id: "disableBookmarkButton",
      pref: "pref.browser.homepage.disable_button.bookmark_page",
    });

    // Homepage / Choose Custom Homepage URL Button
    Preferences.addSetting({
      id: "homepageGoToCustomHomepageUrlPanel",
      deps: ["homepageNewWindows", "homepageDisplayPref"],
      visible: ({ homepageNewWindows }) => {
        return homepageNewWindows.value === "custom";
      },
      onUserClick: () => {
        window.gotoPref("customHomepage");
      },

      getControlConfig(config, { homepageDisplayPref }) {
        let customURLsDescription;

        // Make sure we only show user-provided values for custom URLs rather than
        // values we set in `browser.startup.homepage` for "Firefox Home"
        // and "Blank Page".
        if (
          [DEFAULT_HOMEPAGE_URL, BLANK_HOMEPAGE_URL].includes(
            homepageDisplayPref.value.trim()
          )
        ) {
          customURLsDescription = null;
        } else {
          // Add a comma-separated list of Custom URLs the user set for their homepage
          // to the description part of the "Choose a specific site" box button.
          customURLsDescription = homepageDisplayPref.value
            .split("|")
            .map(uri => lazy.BrowserUtils.formatURIStringForDisplay(uri))
            .filter(Boolean)
            .join(", ");
        }

        return {
          ...config,
          controlAttrs: {
            ...config.controlAttrs,
            ".description": customURLsDescription,
          },
        };
      },
    });

    // Homepage / New Tabs
    Preferences.addSetting({
      id: "homepageNewTabs",
      pref: "browser.newtabpage.enabled",
      get(prefVal) {
        return prefVal.toString();
      },
      set(inputVal) {
        return inputVal === "true";
      },
    });

    // Homepage / Restore Defaults button
    Preferences.addSetting({
      id: "homepageRestoreDefaults",
      pref: "pref.browser.homepage.disable_button.restore_default",
      deps: ["homepageNewWindows", "homepageNewTabs"],
      disabled: ({ homepageNewWindows, homepageNewTabs }) => {
        return (
          homepageNewWindows.value === "home" &&
          homepageNewTabs.value === "true"
        );
      },
      onUserClick: (e, { homepageNewWindows, homepageNewTabs }) => {
        e.preventDefault();

        homepageNewWindows.value = "home";
        homepageNewTabs.value = "true";
      },
    });

    return {
      inProgress: true,
      headingLevel: 2,
      iconSrc: "chrome://browser/skin/window-firefox.svg",
      l10nId: "home-homepage-title",
      items: [
        {
          id: "homepageNewWindows",
          control: "moz-select",
          l10nId: "home-homepage-new-windows",
          options: [
            {
              value: "home",
              l10nId: "home-mode-choice-default-fx",
            },
            { value: "blank", l10nId: "home-mode-choice-blank" },
            { value: "custom", l10nId: "home-mode-choice-custom" },
          ],
        },
        {
          id: "homepageGoToCustomHomepageUrlPanel",
          control: "moz-box-button",
          l10nId: "home-homepage-custom-homepage-button",
        },
        {
          id: "homepageNewTabs",
          control: "moz-select",
          l10nId: "home-homepage-new-tabs",
          options: [
            {
              value: "true",
              l10nId: "home-mode-choice-default-fx",
            },
            { value: "false", l10nId: "home-mode-choice-blank" },
          ],
        },
        {
          id: "homepageRestoreDefaults",
          control: "moz-button",
          iconSrc: "chrome://global/skin/icons/arrow-counterclockwise-16.svg",
          l10nId: "home-restore-defaults",
          controlAttrs: { id: "restoreDefaultHomePageBtn" },
        },
      ],
    };
  }

  /** @param {Window} window */
  _setupCustomHomepageGroup(window) {
    const { Preferences } = window;

    Preferences.addSetting(
      /** @type {{ _inputValue: string } & SettingConfig } */ ({
        id: "customHomepageAddUrlInput",
        _inputValue: "",
        get() {
          return this._inputValue;
        },

        set(val, _, setting) {
          this._inputValue = val.trim();
          setting.onChange();
        },
      })
    );

    Preferences.addSetting({
      id: "customHomepageAddAddressButton",
      deps: ["homepageDisplayPref", "customHomepageAddUrlInput"],
      onUserClick(e, { homepageDisplayPref, customHomepageAddUrlInput }) {
        // Focus is being stolen by a parent component here (moz-fieldset).
        // Focus on the button to get the input value.
        e.target.focus();

        let inputVal = customHomepageAddUrlInput.value;

        // Don't do anything for empty strings
        if (!inputVal) {
          return;
        }

        if (
          [DEFAULT_HOMEPAGE_URL, BLANK_HOMEPAGE_URL].includes(
            homepageDisplayPref.value.trim()
          )
        ) {
          // Replace the default homepage value with the new Custom URL.
          homepageDisplayPref.value = inputVal;
        } else {
          // Append this URL to the list of Custom URLs saved in prefs.
          let urls = lazy.HomePage.parseCustomHomepageURLs(
            homepageDisplayPref.value
          );
          urls.push(inputVal);
          homepageDisplayPref.value = urls.join("|");
        }

        // Reset the field to empty string
        customHomepageAddUrlInput.value = "";
      },
    });

    Preferences.addSetting({
      id: "customHomepageReplaceWithCurrentPagesButton",
      deps: ["homepageDisplayPref", "disableCurrentPagesButton"],
      // Re-evaluate disabled state on tab open/close (add/remove tabs) and
      // pin/unpin (changes what getTabsForCustomHomepage() captures).
      setup(emitChange) {
        let win = /** @type {any} */ (
          Services.wm.getMostRecentWindow("navigator:browser")
        );
        if (!win) {
          return () => {};
        }
        const { tabContainer } = win.gBrowser;
        // Best-effort filter: skip events from tabs already showing about:preferences.
        // TabOpen fires before the URI is set, so it isn't caught here;
        // the real exclusion happens inside getTabsForCustomHomepage().
        const onTabChange = (/** @type {Event & { target: any }} */ event) => {
          if (
            event.target.linkedBrowser?.currentURI?.spec?.startsWith(
              "about:preferences"
            )
          ) {
            return;
          }
          emitChange();
        };
        tabContainer.addEventListener("TabOpen", onTabChange);
        tabContainer.addEventListener("TabClose", onTabChange);
        tabContainer.addEventListener("TabPinned", onTabChange);
        tabContainer.addEventListener("TabUnpinned", onTabChange);
        return () => {
          tabContainer.removeEventListener("TabOpen", onTabChange);
          tabContainer.removeEventListener("TabClose", onTabChange);
          tabContainer.removeEventListener("TabPinned", onTabChange);
          tabContainer.removeEventListener("TabUnpinned", onTabChange);
        };
      },
      onUserClick(e, { homepageDisplayPref }) {
        let tabs = lazy.HomePage.getTabsForCustomHomepage();

        if (tabs.length) {
          homepageDisplayPref.value = tabs
            .map(t => t.linkedBrowser.currentURI.spec)
            .join("|");
        }
      },
      disabled: ({ disableCurrentPagesButton }) =>
        // Disable this button if the only open tab is `about:preferences`/`about:settings`
        // or when an enterprise policy sets a special pref to true
        lazy.HomePage.getTabsForCustomHomepage().length < 1 ||
        disableCurrentPagesButton?.value === true,
    });

    Preferences.addSetting({
      id: "customHomepageReplaceWithBookmarksButton",
      deps: ["homepageDisplayPref", "disableBookmarkButton"],
      onUserClick(e, { homepageDisplayPref }) {
        const rv = { urls: null, names: null };

        // Callback to use when bookmark dialog closes
        const closingCallback = event => {
          if (event.detail.button !== "accept") {
            return;
          }
          if (rv.urls) {
            homepageDisplayPref.value = rv.urls.join("|");
          }
        };

        window.gSubDialog.open(
          "chrome://browser/content/preferences/dialogs/selectBookmark.xhtml",
          {
            features: "resizable=yes, modal=yes",
            closingCallback,
          },
          rv
        );
      },
      disabled: ({ disableBookmarkButton }) =>
        // Disable this button if an enterprise policy sets a special pref to true
        disableBookmarkButton?.value === true,
    });

    Preferences.addSetting({
      id: "customHomepageBoxGroup",
      deps: ["homepageDisplayPref"],
      getControlConfig(config, { homepageDisplayPref }) {
        const urls = lazy.HomePage.parseCustomHomepageURLs(
          homepageDisplayPref.value
        );
        let listItems = [];
        let type = "list";

        // Show a reorderable list of Custom URLs if the user has provided any.
        // Make sure to exclude "Firefox Home" and "Blank Page" values that are also
        // stored in the homepage pref.
        if (
          [DEFAULT_HOMEPAGE_URL, BLANK_HOMEPAGE_URL].includes(
            homepageDisplayPref.value.trim()
          ) === false
        ) {
          type = "reorderable-list";
          listItems = urls.map((url, index) => ({
            id: `customHomepageUrl-${index}`,
            key: `url-${index}-${url}`,
            control: "moz-box-item",
            controlAttrs: { label: url, "data-url": url },
            options: [
              {
                control: "moz-button",
                iconSrc: "chrome://global/skin/icons/delete.svg",
                l10nId: "home-custom-homepage-delete-address-button",
                slot: "actions-start",
                controlAttrs: {
                  "data-action": "delete",
                  "data-index": index,
                },
              },
            ],
          }));
        } else {
          // If no custom URLs have been set, show the "no results" string instead.
          listItems = [
            {
              control: "moz-box-item",
              l10nId: "home-custom-homepage-no-results",
              controlAttrs: {
                class: "description-deemphasized",
              },
            },
          ];
        }

        return {
          ...config,
          controlAttrs: {
            ...config.controlAttrs,
            type,
          },
          options: [
            {
              id: "customHomepageBoxForm",
              control: "moz-box-item",
              slot: "header",
              items: [
                {
                  id: "customHomepageAddUrlInput",
                  l10nId: "home-custom-homepage-address",
                  control: "moz-input-text",
                },
                {
                  id: "customHomepageAddAddressButton",
                  l10nId: "home-custom-homepage-address-button",
                  control: "moz-button",
                  slot: "actions",
                },
              ],
            },
            ...listItems,
            {
              id: "customHomepageBoxActions",
              control: "moz-box-item",
              l10nId: "home-custom-homepage-replace-with-prompt",
              slot: "footer",
              items: [
                {
                  id: "customHomepageReplaceWithCurrentPagesButton",
                  l10nId: "home-custom-homepage-current-pages-button",
                  control: "moz-button",
                  slot: "actions",
                },
                {
                  id: "customHomepageReplaceWithBookmarksButton",
                  l10nId: "home-custom-homepage-bookmarks-button",
                  control: "moz-button",
                  slot: "actions",
                },
              ],
            },
          ],
        };
      },
      onUserReorder(e, { homepageDisplayPref }) {
        let urls = lazy.HomePage.parseCustomHomepageURLs(
          homepageDisplayPref.value
        );

        let { draggedIndex, targetIndex } = e.detail;
        let [moved] = urls.splice(draggedIndex, 1);
        urls.splice(targetIndex, 0, moved);

        homepageDisplayPref.value = urls.join("|");
      },
      onUserClick(e, { homepageDisplayPref }) {
        let urls = lazy.HomePage.parseCustomHomepageURLs(
          homepageDisplayPref.value
        );

        if (
          e.target.localName === "moz-button" &&
          e.target.getAttribute("data-action") === "delete"
        ) {
          let index = Number(e.target.dataset.index);
          if (Number.isInteger(index) && index >= 0 && index < urls.length) {
            urls.splice(index, 1);
            homepageDisplayPref.value = urls.join("|");
          }
        }
      },
    });

    return {
      inProgress: true,
      headingLevel: 2,
      l10nId: "home-custom-homepage-card-header",
      iconSrc: "chrome://global/skin/icons/link.svg",
      items: [
        {
          id: "customHomepageBoxGroup",
          control: "moz-box-group",
          controlAttrs: {
            type: "list",
          },
        },
      ],
    };
  }

  /** @param {Window} window */
  _setupHomeGroup(window) {
    const { Preferences } = window;

    // Search
    Preferences.addSetting({
      id: "webSearch",
      pref: "browser.newtabpage.activity-stream.showSearch",
    });

    // Weather
    Preferences.addSetting({
      id: "showWeather",
      pref: "browser.newtabpage.activity-stream.system.showWeather",
    });

    Preferences.addSetting({
      id: "weather",
      pref: "browser.newtabpage.activity-stream.showWeather",
      deps: ["showWeather"],
      visible: ({ showWeather }) => showWeather.value,
    });

    // Widgets: general
    Preferences.addSetting({
      id: "widgetsEnabled",
      pref: "browser.newtabpage.activity-stream.widgets.system.enabled",
    });

    Preferences.addSetting({
      id: "widgets",
      pref: "browser.newtabpage.activity-stream.widgets.enabled",
      deps: ["widgetsEnabled"],
      visible: ({ widgetsEnabled }) => widgetsEnabled.value,
    });

    // Widgets: lists
    Preferences.addSetting({
      id: "listsEnabled",
      pref: "browser.newtabpage.activity-stream.widgets.system.lists.enabled",
    });

    Preferences.addSetting({
      id: "lists",
      pref: "browser.newtabpage.activity-stream.widgets.lists.enabled",
      deps: ["listsEnabled"],
      visible: ({ listsEnabled }) => listsEnabled.value,
    });

    // Widgets: timer
    Preferences.addSetting({
      id: "timerEnabled",
      pref: "browser.newtabpage.activity-stream.widgets.system.focusTimer.enabled",
    });

    Preferences.addSetting({
      id: "timer",
      pref: "browser.newtabpage.activity-stream.widgets.focusTimer.enabled",
      deps: ["timerEnabled"],
      visible: ({ timerEnabled }) => timerEnabled.value,
    });

    // Shortcuts
    Preferences.addSetting({
      id: "shortcuts",
      pref: "browser.newtabpage.activity-stream.feeds.topsites",
    });
    Preferences.addSetting({
      id: "shortcutsRows",
      pref: "browser.newtabpage.activity-stream.topSitesRows",
    });

    // Dependency prefs for stories & sponsored stories visibility
    Preferences.addSetting({
      id: "systemTopstories",
      pref: "browser.newtabpage.activity-stream.feeds.system.topstories",
    });

    // Stories
    Preferences.addSetting({
      id: "stories",
      pref: "browser.newtabpage.activity-stream.feeds.section.topstories",
      deps: ["systemTopstories"],
      visible: ({ systemTopstories }) => systemTopstories.value,
    });

    // Dependencies for "manage topics" checkbox
    Preferences.addSetting({
      id: "sectionsEnabled",
      pref: "browser.newtabpage.activity-stream.discoverystream.sections.enabled",
    });
    Preferences.addSetting({
      id: "topicLabelsEnabled",
      pref: "browser.newtabpage.activity-stream.discoverystream.topicLabels.enabled",
    });
    Preferences.addSetting({
      id: "sectionsPersonalizationEnabled",
      pref: "browser.newtabpage.activity-stream.discoverystream.sections.personalization.enabled",
    });
    Preferences.addSetting({
      id: "sectionsCustomizeMenuPanelEnabled",
      pref: "browser.newtabpage.activity-stream.discoverystream.sections.customizeMenuPanel.enabled",
    });

    Preferences.addSetting({
      id: "manageTopics",
      deps: [
        "sectionsEnabled",
        "topicLabelsEnabled",
        "sectionsPersonalizationEnabled",
        "sectionsCustomizeMenuPanelEnabled",
        "stories",
      ],
      visible: ({
        sectionsEnabled,
        topicLabelsEnabled,
        sectionsPersonalizationEnabled,
        sectionsCustomizeMenuPanelEnabled,
        stories,
      }) =>
        sectionsEnabled.value &&
        topicLabelsEnabled.value &&
        sectionsPersonalizationEnabled.value &&
        sectionsCustomizeMenuPanelEnabled.value &&
        stories.value,
    });

    // Support Firefox: sponsored content
    Preferences.addSetting({
      id: "supportFirefox",
      pref: "browser.newtabpage.activity-stream.showSponsoredCheckboxes",
      deps: ["sponsoredShortcuts", "sponsoredStories"],
      onUserChange(value, { sponsoredShortcuts, sponsoredStories }) {
        // When supportFirefox changes, automatically update child preferences to match
        sponsoredShortcuts.value = !!value;
        sponsoredStories.value = !!value;
      },
    });
    Preferences.addSetting({
      id: "topsitesEnabled",
      pref: "browser.newtabpage.activity-stream.feeds.topsites",
    });
    Preferences.addSetting({
      id: "sponsoredShortcuts",
      pref: "browser.newtabpage.activity-stream.showSponsoredTopSites",
      deps: ["topsitesEnabled"],
      disabled: ({ topsitesEnabled }) => !topsitesEnabled.value,
    });
    Preferences.addSetting({
      id: "sponsoredStories",
      pref: "browser.newtabpage.activity-stream.showSponsored",
      deps: ["systemTopstories", "stories"],
      visible: ({ systemTopstories }) => !!systemTopstories.value,
      disabled: ({ stories }) => !stories.value,
    });
    Preferences.addSetting({
      id: "supportFirefoxPromo",
      deps: ["supportFirefox"],
    });

    // Recent activity
    Preferences.addSetting({
      id: "recentActivity",
      pref: "browser.newtabpage.activity-stream.feeds.section.highlights",
    });
    Preferences.addSetting({
      id: "recentActivityRows",
      pref: "browser.newtabpage.activity-stream.section.highlights.rows",
    });
    Preferences.addSetting({
      id: "recentActivityVisited",
      pref: "browser.newtabpage.activity-stream.section.highlights.includeVisited",
    });
    Preferences.addSetting({
      id: "recentActivityBookmarks",
      pref: "browser.newtabpage.activity-stream.section.highlights.includeBookmarks",
    });
    Preferences.addSetting({
      id: "recentActivityDownloads",
      pref: "browser.newtabpage.activity-stream.section.highlights.includeDownloads",
    });

    Preferences.addSetting({
      id: "chooseWallpaper",
    });

    return {
      inProgress: true,
      headingLevel: 2,
      l10nId: "home-prefs-content-header",
      iconSrc: "chrome://browser/skin/home.svg",
      items: [
        {
          id: "webSearch",
          l10nId: "home-prefs-search-header2",
          control: "moz-toggle",
        },
        {
          id: "weather",
          l10nId: "home-prefs-weather-header",
          control: "moz-toggle",
        },
        {
          id: "widgets",
          l10nId: "home-prefs-widgets-header",
          control: "moz-toggle",
          items: [
            {
              id: "lists",
              l10nId: "home-prefs-lists-header",
            },
            {
              id: "timer",
              l10nId: "home-prefs-timer-header",
            },
          ],
        },
        {
          id: "shortcuts",
          l10nId: "home-prefs-shortcuts-header",
          control: "moz-toggle",
          items: [
            {
              id: "shortcutsRows",
              control: "moz-select",
              options: [
                {
                  value: 1,
                  l10nId: "home-prefs-sections-rows-option",
                  l10nArgs: { num: 1 },
                },
                {
                  value: 2,
                  l10nId: "home-prefs-sections-rows-option",
                  l10nArgs: { num: 2 },
                },
                {
                  value: 3,
                  l10nId: "home-prefs-sections-rows-option",
                  l10nArgs: { num: 3 },
                },
                {
                  value: 4,
                  l10nId: "home-prefs-sections-rows-option",
                  l10nArgs: { num: 4 },
                },
              ],
            },
          ],
        },
        {
          id: "stories",
          l10nId: "home-prefs-stories-header2",
          control: "moz-toggle",
          items: [
            {
              id: "manageTopics",
              l10nId: "home-prefs-manage-topics-link2",
              control: "moz-box-link",
              controlAttrs: {
                href: "about:newtab#customize-topics",
              },
            },
          ],
        },
        {
          id: "supportFirefox",
          l10nId: "home-prefs-support-firefox-header",
          control: "moz-toggle",
          items: [
            {
              id: "sponsoredShortcuts",
              l10nId: "home-prefs-shortcuts-by-option-sponsored",
            },
            {
              id: "sponsoredStories",
              l10nId: "home-prefs-recommended-by-option-sponsored-stories",
            },
            {
              id: "supportFirefoxPromo",
              l10nId: "home-prefs-mission-message2",
              control: "moz-promo",
              options: [
                {
                  control: "a",
                  l10nId: "home-prefs-mission-message-learn-more-link",
                  slot: "support-link",
                  controlAttrs: {
                    is: "moz-support-link",
                    "support-page": "sponsor-privacy",
                    "utm-content": "inproduct",
                  },
                },
              ],
            },
          ],
        },
        {
          id: "recentActivity",
          l10nId: "home-prefs-recent-activity-header",
          control: "moz-toggle",
          items: [
            {
              id: "recentActivityRows",
              control: "moz-select",
              options: [
                {
                  value: 1,
                  l10nId: "home-prefs-sections-rows-option",
                  l10nArgs: { num: 1 },
                },
                {
                  value: 2,
                  l10nId: "home-prefs-sections-rows-option",
                  l10nArgs: { num: 2 },
                },
                {
                  value: 3,
                  l10nId: "home-prefs-sections-rows-option",
                  l10nArgs: { num: 3 },
                },
                {
                  value: 4,
                  l10nId: "home-prefs-sections-rows-option",
                  l10nArgs: { num: 4 },
                },
              ],
            },
            {
              id: "recentActivityVisited",
              l10nId: "home-prefs-highlights-option-visited-pages",
            },
            {
              id: "recentActivityBookmarks",
              l10nId: "home-prefs-highlights-options-bookmarks",
            },
            {
              id: "recentActivityDownloads",
              l10nId: "home-prefs-highlights-option-most-recent-download",
            },
          ],
        },
        {
          id: "chooseWallpaper",
          l10nId: "home-prefs-choose-wallpaper-link2",
          control: "moz-box-link",
          controlAttrs: {
            href: "about:newtab#customize",
          },
          iconSrc: "chrome://browser/skin/customize.svg",
        },
      ],
    };
  }

  /**
   * Render a single preference with all the details, e.g. description, links,
   * more granular preferences.
   *
   * @param sectionData
   * @param document
   * @param Preferences
   */

  /**
   * We can remove this eslint exception once the Settings redesign is complete.
   * In fact, we can probably remove this entire method.
   */
  // eslint-disable-next-line max-statements
  renderPreferenceSection(sectionData, document, Preferences) {
    /* Do not render old-style settings if new settings UI is enabled - this is needed to avoid
     * registering prefs twice and ensuing errors */
    if (Services.prefs.getBoolPref("browser.settings-redesign.enabled")) {
      return;
    }

    const {
      id,
      pref: prefData,
      maxRows,
      rowsPref,
      shouldHidePref,
      eventSource,
    } = sectionData;
    const {
      feed: name,
      titleString = {},
      descString,
      nestedPrefs = [],
    } = prefData || {};

    // Helper to link a UI element to a preference for updating
    const linkPref = (element, prefName, type) => {
      const fullPref = `browser.newtabpage.activity-stream.${prefName}`;
      element.setAttribute("preference", fullPref);
      Preferences.add({ id: fullPref, type });

      // Prevent changing the UI if the preference can't be changed
      element.disabled = Preferences.get(fullPref).locked;
    };

    // Don't show any sections that we don't want to expose in preferences UI
    if (shouldHidePref) {
      return;
    }

    // Add the main preference for turning on/off a section
    const sectionVbox = document.getElementById(id);
    sectionVbox.setAttribute("data-subcategory", id);
    const checkbox = this.createAppend(document, "checkbox", sectionVbox);
    checkbox.classList.add("section-checkbox");
    // Set up a user event if we have an event source for this pref.
    if (eventSource) {
      this.setupUserEvent(checkbox, eventSource);
    }
    document.l10n.setAttributes(
      checkbox,
      this.getString(titleString),
      titleString.values
    );

    linkPref(checkbox, name, "bool");

    // Specially add a link for Weather
    if (id === "weather") {
      const hboxWithLink = this.createAppend(document, "hbox", sectionVbox);
      hboxWithLink.appendChild(checkbox);
      checkbox.classList.add("tail-with-learn-more");

      const link = this.createAppend(document, "label", hboxWithLink, {
        is: "text-link",
      });
      link.setAttribute("href", sectionData.pref.learnMore.link.href);
      document.l10n.setAttributes(link, sectionData.pref.learnMore.link.id);
    }

    // Add more details for the section (e.g., description, more prefs)
    const detailVbox = this.createAppend(document, "vbox", sectionVbox);
    detailVbox.classList.add("indent");
    if (descString) {
      const description = this.createAppend(
        document,
        "description",
        detailVbox
      );
      description.classList.add("text-deemphasized");
      document.l10n.setAttributes(
        description,
        this.getString(descString),
        descString.values
      );

      // Add a rows dropdown if we have a pref to control and a maximum
      if (rowsPref && maxRows) {
        const detailHbox = this.createAppend(document, "hbox", detailVbox);
        detailHbox.setAttribute("align", "center");
        description.setAttribute("flex", 1);
        detailHbox.appendChild(description);

        // Add box so the search tooltip is positioned correctly
        const tooltipBox = this.createAppend(document, "hbox", detailHbox);

        // Add appropriate number of localized entries to the dropdown
        const menulist = this.createAppend(document, "menulist", tooltipBox);
        menulist.setAttribute("crop", "none");
        const menupopup = this.createAppend(document, "menupopup", menulist);
        for (let num = 1; num <= maxRows; num++) {
          const item = this.createAppend(document, "menuitem", menupopup);
          document.l10n.setAttributes(item, "home-prefs-sections-rows-option", {
            num,
          });
          item.setAttribute("value", num);
        }
        linkPref(menulist, rowsPref, "int");
      }
    }

    const subChecks = [];
    const fullName = `browser.newtabpage.activity-stream.${sectionData.pref.feed}`;
    const pref = Preferences.get(fullName);

    // Add a checkbox pref for any nested preferences
    nestedPrefs.forEach(nested => {
      if (nested.shouldHidePref !== true) {
        const subcheck = this.createAppend(document, "checkbox", detailVbox);
        // Set up a user event if we have an event source for this pref.
        if (nested.eventSource) {
          this.setupUserEvent(subcheck, nested.eventSource);
        }
        document.l10n.setAttributes(subcheck, nested.titleString);

        linkPref(subcheck, nested.name, "bool");

        subChecks.push(subcheck);
        subcheck.disabled = !pref._value;
        if (nested.shouldDisablePref) {
          subcheck.disabled = nested.shouldDisablePref;
        }
        subcheck.hidden = nested.hidden;
      }
    });

    // Special cases to like the nested prefs with another pref,
    // so we can disable it real time.
    if (id === "support-firefox") {
      function setupSupportFirefoxSubCheck(triggerPref, subPref) {
        const subCheckFullName = `browser.newtabpage.activity-stream.${triggerPref}`;
        const subCheckPref = Preferences.get(subCheckFullName);

        subCheckPref?.on("change", () => {
          const showSponsoredFullName = `browser.newtabpage.activity-stream.${subPref}`;
          const showSponsoredSubcheck = subChecks.find(
            subcheck =>
              subcheck.getAttribute("preference") === showSponsoredFullName
          );
          if (showSponsoredSubcheck) {
            showSponsoredSubcheck.disabled = !Services.prefs.getBoolPref(
              subCheckFullName,
              true
            );
          }
        });
      }

      setupSupportFirefoxSubCheck("feeds.section.topstories", "showSponsored");
      setupSupportFirefoxSubCheck("feeds.topsites", "showSponsoredTopSites");
    }

    pref.on("change", () => {
      subChecks.forEach(subcheck => {
        // Update child preferences for the "Support Firefox" checkbox group
        // so that they're turned on and off at the same time.
        if (id === "support-firefox") {
          const subPref = Preferences.get(subcheck.getAttribute("preference"));
          subPref.value = pref.value;
        }

        // Disable any nested checkboxes if the parent pref is not enabled.
        subcheck.disabled = !pref._value;
      });
    });
  }

  /**
   * Update the visibility of the Restore Defaults button based on checked prefs.
   *
   * @param gHomePane
   */
  toggleRestoreDefaults(gHomePane) {
    gHomePane.toggleRestoreDefaultsBtn();
  }

  /**
   * A helper function to append XUL elements on the page.
   *
   * @param document
   * @param tag
   * @param parent
   * @param options
   */
  createAppend(document, tag, parent, options = {}) {
    return parent.appendChild(document.createXULElement(tag, options));
  }

  /**
   * Helper to get fluentIDs sometimes encase in an object
   *
   * @param message
   * @returns string
   */
  getString(message) {
    return typeof message !== "object" ? message : message.id;
  }
}
