// 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/.
var gZenMarketplaceManager = {
  async init() {
    const checkForUpdates = document.getElementById('zenThemeMarketplaceCheckForUpdates');
    const header = document.getElementById('zenMarketplaceHeader');

    if (!checkForUpdates || !header) {
      return; // We haven't entered the settings page yet.
    }

    if (this.__hasInitializedEvents) {
      return;
    }

    if (!window.gZenMods) {
      window.gZenMods = ZenMultiWindowFeature.currentBrowser.gZenMods;
    }

    header.appendChild(this._initDisableAll());

    this._initImportExport();

    this.__hasInitializedEvents = true;

    await this._buildModsList();

    Services.prefs.addObserver(gZenMods.updatePref, this);

    const checkForUpdateClick = (event) => {
      if (event.target === checkForUpdates) {
        event.preventDefault();

        this._checkForThemeUpdates(event);
      }
    };

    checkForUpdates.addEventListener('click', checkForUpdateClick);

    document.addEventListener('ZenModsMarketplace:CheckForUpdatesFinished', (event) => {
      checkForUpdates.disabled = false;

      const updates = event.detail.updates;
      const success = document.getElementById('zenThemeMarketplaceUpdatesSuccess');
      const error = document.getElementById('zenThemeMarketplaceUpdatesFailure');

      if (updates) {
        success.hidden = false;
        error.hidden = true;
      } else {
        success.hidden = true;
        error.hidden = false;
      }
    });

    window.addEventListener('unload', () => {
      Services.prefs.removeObserver(gZenMods.updatePref, this);
      this.__hasInitializedEvents = false;

      document.removeEventListener('ZenModsMarketplace:CheckForUpdatesFinished', this);
      document.removeEventListener('ZenCheckForModUpdates', this);

      checkForUpdates.removeEventListener('click', checkForUpdateClick);

      this.modsList.innerHTML = '';
      this._doNotRebuildModsList = false;
    });
  },

  _initImportExport() {
    const importButton = document.getElementById('zenThemeMarketplaceImport');
    const exportButton = document.getElementById('zenThemeMarketplaceExport');

    if (importButton) {
      importButton.addEventListener('click', this._importThemes.bind(this));
    }

    if (exportButton) {
      exportButton.addEventListener('click', this._exportThemes.bind(this));
    }
  },

  _initDisableAll() {
    const areModsDisabled = Services.prefs.getBoolPref('zen.themes.disable-all', false);
    const browser = ZenMultiWindowFeature.currentBrowser;
    const mozToggle = document.createElement('moz-toggle');

    mozToggle.className =
      'zenThemeMarketplaceItemPreferenceToggle zenThemeMarketplaceDisableAllToggle';
    mozToggle.pressed = !areModsDisabled;

    browser.document.l10n.setAttributes(
      mozToggle,
      `zen-theme-disable-all-${!areModsDisabled ? 'enabled' : 'disabled'}`
    );

    mozToggle.addEventListener('toggle', async (event) => {
      const { pressed = false } = event.target || {};

      this.modsList.style.display = pressed ? '' : 'none';
      Services.prefs.setBoolPref('zen.themes.disable-all', !pressed);
      browser.document.l10n.setAttributes(
        mozToggle,
        `zen-theme-disable-all-${pressed ? 'enabled' : 'disabled'}`
      );
    });

    if (areModsDisabled) {
      this.modsList.style.display = 'none';
    }

    return mozToggle;
  },

  async observe() {
    await this._buildModsList();
  },

  _checkForThemeUpdates(event) {
    // Send a message to the child to check for theme updates.
    event.target.disabled = true;
    // send an event that will be listened by the child process.
    document.dispatchEvent(new CustomEvent('ZenCheckForModUpdates'));
  },

  get modsList() {
    if (!this._modsList) {
      this._modsList = document.getElementById('zenThemeMarketplaceList');
    }
    return this._modsList;
  },

  _triggerBuildUpdateWithoutRebuild() {
    this._doNotRebuildModsList = true;
    gZenMods.triggerModsUpdate();
  },

  async removeMod(modId) {
    await gZenMods.removeMod(modId);

    gZenMods.triggerModsUpdate();
  },

  async disableMod(modId) {
    await gZenMods.disableMod(modId);

    this._triggerBuildUpdateWithoutRebuild();
  },

  async enableMod(modId) {
    await gZenMods.enableMod(modId);

    this._triggerBuildUpdateWithoutRebuild();
  },

  async _importThemes() {
    const errorBox = document.getElementById('zenThemeMarketplaceImportFailure');
    const successBox = document.getElementById('zenThemeMarketplaceImportSuccess');

    successBox.hidden = true;
    errorBox.hidden = true;

    const input = document.createElement('input');

    input.type = 'file';
    input.accept = '.json';
    input.style.display = 'none';
    input.setAttribute('moz-accept', '.json');
    input.setAttribute('accept', '.json');

    let timeout;

    const filePromise = new Promise((resolve) => {
      input.addEventListener('change', (event) => {
        if (timeout) {
          clearTimeout(timeout);
        }

        const file = event.target.files[0];
        resolve(file);
      });

      timeout = setTimeout(() => {
        console.warn('[ZenSettings:ZenMods]: Import timeout reached, aborting.');
        resolve(null);
      }, 60000);
    });

    input.addEventListener('cancel', () => {
      console.warn('[ZenSettings:ZenMods]: Import cancelled by user.');
      clearTimeout(timeout);
    });

    input.click();

    try {
      const file = await filePromise;

      if (!file) {
        return;
      }

      const content = await file.text();

      const mods = JSON.parse(content);

      for (const mod of Object.values(mods)) {
        mod.modId = mod.id;
        await window.ZenInstallMod(mod);
      }
    } catch (error) {
      console.error('[ZenSettings:ZenMods]: Error while importing mods:', error);
      errorBox.hidden = false;
    }

    if (input) {
      input.remove();
    }
  },

  async _exportThemes() {
    const errorBox = document.getElementById('zenThemeMarketplaceExportFailure');
    const successBox = document.getElementById('zenThemeMarketplaceExportSuccess');

    successBox.hidden = true;
    errorBox.hidden = true;

    let temporalAnchor, temporalUrl;
    try {
      const mods = await gZenMods.getMods();
      const modsJson = JSON.stringify(mods, null, 2);
      const blob = new Blob([modsJson], { type: 'application/json' });

      temporalUrl = URL.createObjectURL(blob);
      // Creating a link to download the JSON file
      temporalAnchor = document.createElement('a');
      temporalAnchor.href = temporalUrl;
      temporalAnchor.download = 'zen-mods-export.json';

      document.body.appendChild(temporalAnchor);
      temporalAnchor.click();
      temporalAnchor.remove();

      successBox.hidden = false;
    } catch (error) {
      console.error('[ZenSettings:ZenMods]: Error while exporting mods:', error);
      errorBox.hidden = false;
    }

    if (temporalAnchor) {
      temporalAnchor.remove();
    }

    if (temporalUrl) {
      URL.revokeObjectURL(temporalUrl);
    }
  },

  async _buildModsList() {
    if (!this.modsList) {
      return;
    }

    if (this._doNotRebuildModsList) {
      this._doNotRebuildModsList = false;
      return;
    }

    const mods = await gZenMods.getMods();
    const browser = ZenMultiWindowFeature.currentBrowser;
    const modList = document.createElement('div');

    for (const mod of Object.values(mods).sort((a, b) => a.name.localeCompare(b.name))) {
      const sanitizedName = gZenMods.sanitizeModName(mod.name);
      const isModEnabled = mod.enabled === undefined || mod.enabled;
      const fragment = window.MozXULElement.parseXULToFragment(`
        <vbox class="zenThemeMarketplaceItem">
          <vbox class="zenThemeMarketplaceItemContent">
            <hbox flex="1" id="zenThemeMarketplaceItemContentHeader">
              <label><h3 class="zenThemeMarketplaceItemTitle"></h3></label>
            </hbox>
            <description class="description-deemphasized zenThemeMarketplaceItemDescription"></description>
          </vbox>
          <hbox class="zenThemeMarketplaceItemActions">
            ${mod.preferences ? `<button id="zenThemeMarketplaceItemConfigureButton-${sanitizedName}" class="zenThemeMarketplaceItemConfigureButton" hidden="true"></button>` : ''}
            ${mod.homepage ? `<button id="zenThemeMarketplaceItemHomePageLink-${sanitizedName}" class="zenThemeMarketplaceItemHomepageButton" zen-mod-id="${mod.id}"></button>` : ''}
            <button class="zenThemeMarketplaceItemUninstallButton" data-l10n-id="zen-theme-marketplace-remove-button" zen-mod-id="${mod.id}"></button>
          </hbox>
        </vbox>
      `);

      const modName = `${mod.name} (v${mod.version ?? '1.0.0'})`;

      const base = fragment.querySelector('.zenThemeMarketplaceItem');
      const baseHeader = fragment.querySelector('#zenThemeMarketplaceItemContentHeader');

      const dialog = document.createElement('dialog');
      const mainDialogDiv = document.createElement('div');
      const headerDiv = document.createElement('div');
      const headerTitle = document.createElement('h3');
      const closeButton = document.createElement('button');
      const contentDiv = document.createElement('div');
      const mozToggle = document.createElement('moz-toggle');

      mainDialogDiv.className = 'zenThemeMarketplaceItemPreferenceDialog';
      headerDiv.className = 'zenThemeMarketplaceItemPreferenceDialogTopBar';
      headerTitle.textContent = modName;
      browser.document.l10n.setAttributes(headerTitle, 'zen-theme-marketplace-theme-header-title', {
        name: sanitizedName,
      });
      headerTitle.className = 'zenThemeMarketplaceItemTitle';
      closeButton.id = `${sanitizedName}-modal-close`;
      browser.document.l10n.setAttributes(closeButton, 'zen-theme-marketplace-close-modal');
      contentDiv.id = `${sanitizedName}-preferences-content`;
      contentDiv.className = 'zenThemeMarketplaceItemPreferenceDialogContent';
      mozToggle.className = 'zenThemeMarketplaceItemPreferenceToggle';

      mozToggle.pressed = isModEnabled;
      browser.document.l10n.setAttributes(
        mozToggle,
        `zen-theme-marketplace-toggle-${isModEnabled ? 'enabled' : 'disabled'}-button`
      );

      baseHeader.appendChild(mozToggle);

      headerDiv.appendChild(headerTitle);
      headerDiv.appendChild(closeButton);

      mainDialogDiv.appendChild(headerDiv);
      mainDialogDiv.appendChild(contentDiv);
      dialog.appendChild(mainDialogDiv);
      base.appendChild(dialog);

      closeButton.addEventListener('click', () => {
        dialog.close();
      });

      mozToggle.addEventListener('toggle', async (event) => {
        const modId = event.target
          .closest('.zenThemeMarketplaceItem')
          .querySelector('.zenThemeMarketplaceItemUninstallButton')
          .getAttribute('zen-mod-id');
        event.target.setAttribute('disabled', true);

        if (!event.target.hasAttribute('pressed')) {
          await this.disableMod(modId);

          browser.document.l10n.setAttributes(
            mozToggle,
            'zen-theme-marketplace-toggle-disabled-button'
          );

          if (mod.preferences) {
            document
              .getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`)
              .setAttribute('hidden', true);
          }
        } else {
          await this.enableMod(modId);

          browser.document.l10n.setAttributes(
            mozToggle,
            'zen-theme-marketplace-toggle-enabled-button'
          );

          if (mod.preferences) {
            document
              .getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`)
              .removeAttribute('hidden');
          }
        }
        setTimeout(() => {
          // We use a timeout to make sure the theme list has been updated before re-enabling the button.
          event.target.removeAttribute('disabled');
        }, 400);
      });

      fragment.querySelector('.zenThemeMarketplaceItemTitle').textContent = modName;
      fragment.querySelector('.zenThemeMarketplaceItemDescription').textContent = mod.description;
      fragment
        .querySelector('.zenThemeMarketplaceItemUninstallButton')
        .addEventListener('click', async (event) => {
          const [msg] = await document.l10n.formatValues([
            { id: 'zen-theme-marketplace-remove-confirmation' },
          ]);

          if (!confirm(msg)) {
            return;
          }

          await this.removeMod(event.target.getAttribute('zen-mod-id'));
        });

      if (mod.homepage) {
        const homepageButton = fragment.querySelector('.zenThemeMarketplaceItemHomepageButton');
        homepageButton.addEventListener('click', () => {
          // open the homepage url in a new tab
          const url = mod.homepage;

          window.open(url, '_blank');
        });
      }

      if (mod.preferences) {
        fragment
          .querySelector('.zenThemeMarketplaceItemConfigureButton')
          .addEventListener('click', () => {
            dialog.showModal();
          });

        if (isModEnabled) {
          fragment
            .querySelector('.zenThemeMarketplaceItemConfigureButton')
            .removeAttribute('hidden');
        }
      }

      const preferences = await gZenMods.getModPreferences(mod);

      if (preferences.length > 0) {
        const preferencesWrapper = document.createXULElement('vbox');

        preferencesWrapper.setAttribute('flex', '1');

        for (const entry of preferences) {
          const { property, label, type, placeholder, defaultValue } = entry;

          switch (type) {
            case 'dropdown': {
              const { options } = entry;

              const container = document.createXULElement('hbox');
              container.classList.add('zenThemeMarketplaceItemPreference');
              container.setAttribute('align', 'center');
              container.setAttribute('role', 'group');

              const menulist = document.createXULElement('menulist');
              const menupopup = document.createXULElement('menupopup');

              menulist.setAttribute('sizetopopup', 'none');
              menulist.setAttribute('id', property + '-popup-menulist');

              const savedValue = Services.prefs.getStringPref(property, defaultValue ?? 'none');

              menulist.setAttribute('value', savedValue);
              menulist.setAttribute('tooltiptext', property);

              const defaultItem = document.createXULElement('menuitem');

              defaultItem.setAttribute('value', 'none');

              if (placeholder) {
                defaultItem.setAttribute('label', placeholder || '-');
              } else {
                browser.document.l10n.setAttributes(
                  defaultItem,
                  'zen-theme-marketplace-dropdown-default-label'
                );
              }

              menupopup.appendChild(defaultItem);

              for (const option of options) {
                const { label, value } = option;

                const valueType = typeof value;

                if (!['string', 'number'].includes(valueType)) {
                  console.log(
                    `[ZenSettings:ZenMods]: Warning, invalid data type received (${valueType}), skipping.`
                  );
                  continue;
                }

                const menuitem = document.createXULElement('menuitem');

                menuitem.setAttribute('value', value.toString());
                menuitem.setAttribute('label', label);

                menupopup.appendChild(menuitem);
              }

              menulist.appendChild(menupopup);

              menulist.addEventListener('command', () => {
                const value = menulist.selectedItem.value;

                let element = browser.document.getElementById(sanitizedName);

                if (!element) {
                  element = browser.document.createElement('div');

                  element.style.display = 'none';
                  element.setAttribute('id', sanitizedName);

                  browser.document.body.appendChild(element);
                }

                element.setAttribute(property?.replaceAll(/\./g, '-'), value);

                Services.prefs.setStringPref(property, value === 'none' ? '' : value);
                this._triggerBuildUpdateWithoutRebuild();
              });

              const nameLabel = document.createXULElement('label');
              nameLabel.setAttribute('flex', '1');
              nameLabel.setAttribute('class', 'zenThemeMarketplaceItemPreferenceLabel');
              nameLabel.setAttribute('value', label);
              nameLabel.setAttribute('tooltiptext', property);

              container.appendChild(nameLabel);
              container.appendChild(menulist);
              container.setAttribute('aria-labelledby', label);

              preferencesWrapper.appendChild(container);
              break;
            }

            case 'checkbox': {
              const checkbox = window.MozXULElement.parseXULToFragment(`
                <hbox class="zenThemeMarketplaceItemPreference">
                  <checkbox class="zenThemeMarketplaceItemPreferenceCheckbox"></checkbox>
                </hbox>
              `);

              const checkboxElement = checkbox.querySelector(
                '.zenThemeMarketplaceItemPreferenceCheckbox'
              );
              checkboxElement.setAttribute('label', label);
              checkboxElement.setAttribute('tooltiptext', property);
              checkboxElement.setAttribute('zen-pref', property);

              // Checkbox only works with "true" and "false" values, it's not like HTML checkboxes.
              if (Services.prefs.getBoolPref(property, defaultValue ?? false)) {
                checkboxElement.setAttribute('checked', 'true');
              }

              checkboxElement.addEventListener('click', (event) => {
                const target = event.target.closest('.zenThemeMarketplaceItemPreferenceCheckbox');
                const key = target.getAttribute('zen-pref');
                const checked = target.hasAttribute('checked');

                if (!checked) {
                  target.removeAttribute('checked');
                } else {
                  target.setAttribute('checked', 'true');
                }

                Services.prefs.setBoolPref(key, !checked);
              });

              preferencesWrapper.appendChild(checkbox);
              break;
            }

            case 'string': {
              const container = document.createXULElement('hbox');
              container.classList.add('zenThemeMarketplaceItemPreference');
              container.setAttribute('align', 'center');
              container.setAttribute('role', 'group');

              const savedValue = Services.prefs.getStringPref(property, defaultValue ?? '');
              const sanitizedProperty = property?.replaceAll(/\./g, '-');

              const input = document.createElement('input');
              input.setAttribute('flex', '1');
              input.setAttribute('type', 'text');
              input.id = `${sanitizedProperty}-input`;
              input.value = savedValue;

              if (placeholder) {
                input.setAttribute('placeholder', placeholder || '-');
              } else {
                browser.document.l10n.setAttributes(
                  input,
                  'zen-theme-marketplace-input-default-placeholder'
                );
              }

              input.addEventListener(
                'change',
                gZenMods.debounce((event) => {
                  const value = event.target.value;

                  Services.prefs.setStringPref(property, value);
                  this._triggerBuildUpdateWithoutRebuild();

                  if (value === '') {
                    browser.document
                      .querySelector(':root')
                      .style.removeProperty(`--${sanitizedProperty}`);
                  } else {
                    browser.document
                      .querySelector(':root')
                      .style.setProperty(`--${sanitizedProperty}`, value);
                  }
                }, 500)
              );

              const nameLabel = document.createXULElement('label');
              nameLabel.setAttribute('flex', '1');
              nameLabel.setAttribute('class', 'zenThemeMarketplaceItemPreferenceLabel');
              nameLabel.setAttribute('value', label);
              nameLabel.setAttribute('tooltiptext', property);

              container.appendChild(nameLabel);
              container.appendChild(input);
              container.setAttribute('aria-labelledby', label);

              preferencesWrapper.appendChild(container);
              break;
            }

            default:
              console.log(
                `[ZenSettings:ZenMods]: Warning, unknown preference type received (${type}), skipping.`
              );
              continue;
          }
        }
        contentDiv.appendChild(preferencesWrapper);
      }
      modList.appendChild(fragment);
    }

    this.modsList.replaceChildren(...modList.children);
    modList.remove();
  },
};

const kZenExtendedSidebar = 'zen.view.sidebar-expanded';
const kZenSingleToolbar = 'zen.view.use-single-toolbar';

var gZenLooksAndFeel = {
  init() {
    if (this.__hasInitialized) return;
    this.__hasInitialized = true;
    gZenMarketplaceManager.init();
    for (const pref of [kZenExtendedSidebar, kZenSingleToolbar]) {
      Services.prefs.addObserver(pref, this);
    }
    window.addEventListener('unload', () => {
      for (const pref of [kZenExtendedSidebar, kZenSingleToolbar]) {
        Services.prefs.removeObserver(pref, this);
      }
    });
    this.setCompactModeStyle();

    this.applySidebarLayout();
  },

  observe(subject, topic, data) {
    this.applySidebarLayout();
  },

  applySidebarLayout() {
    const isSingleToolbar = Services.prefs.getBoolPref(kZenSingleToolbar);
    const isExtendedSidebar = Services.prefs.getBoolPref(kZenExtendedSidebar);
    for (const layout of document.getElementById('zenLayoutList').children) {
      layout.classList.remove('selected');
      if (layout.getAttribute('layout') == 'single' && isSingleToolbar) {
        layout.classList.add('selected');
      } else if (
        layout.getAttribute('layout') == 'multiple' &&
        !isSingleToolbar &&
        isExtendedSidebar
      ) {
        layout.classList.add('selected');
      } else if (layout.getAttribute('layout') == 'collapsed' && !isExtendedSidebar) {
        layout.classList.add('selected');
      }
    }
    if (this.__hasInitializedLayout) return;
    this.__hasInitializedLayout = true;
    for (const layout of document.getElementById('zenLayoutList').children) {
      layout.addEventListener('click', () => {
        if (layout.hasAttribute('disabled')) {
          return;
        }

        for (const el of document.getElementById('zenLayoutList').children) {
          el.classList.remove('selected');
        }

        layout.classList.add('selected');

        Services.prefs.setBoolPref(
          kZenExtendedSidebar,
          layout.getAttribute('layout') != 'collapsed'
        );
        Services.prefs.setBoolPref(kZenSingleToolbar, layout.getAttribute('layout') == 'single');
      });
    }
  },

  setCompactModeStyle() {
    const chooser = document.getElementById('zen-compact-mode-styles-form');
    const radios = [...chooser.querySelectorAll('input')];

    let value = '';
    if (
      Services.prefs.getBoolPref('zen.view.compact.hide-tabbar', false) &&
      Services.prefs.getBoolPref('zen.view.compact.hide-toolbar', false)
    ) {
      value = 'both';
    } else {
      value = Services.prefs.getBoolPref('zen.view.compact.hide-tabbar') ? 'left' : 'top';
    }
    chooser.querySelector(`[value='${value}']`).checked = true;
    for (let radio of radios) {
      radio.addEventListener('change', (e) => {
        let value = e.target.value;
        switch (value) {
          case 'left':
            Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', true);
            Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', false);
            break;
          case 'top':
            Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', false);
            Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', true);
            break;
          default:
            Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', true);
            Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', true);
            break;
        }
      });
    }
  },
};

var gZenWorkspacesSettings = {
  init() {
    var tabsUnloaderPrefListener = {
      async observe(subject, topic, data) {
        let buttonIndex = await confirmRestartPrompt(true, 1, true, true);
        if (buttonIndex == CONFIRM_RESTART_PROMPT_RESTART_NOW) {
          Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
        }
      },
    };
    Services.prefs.addObserver('zen.tab-unloader.enabled', tabsUnloaderPrefListener);
    Services.prefs.addObserver('zen.glance.enabled', tabsUnloaderPrefListener); // We can use the same listener for both prefs
    Services.prefs.addObserver('zen.workspaces.separate-essentials', tabsUnloaderPrefListener);
    Services.prefs.addObserver('zen.glance.activation-method', tabsUnloaderPrefListener);
    window.addEventListener('unload', () => {
      Services.prefs.removeObserver('zen.tab-unloader.enabled', tabsUnloaderPrefListener);
      Services.prefs.removeObserver('zen.glance.enabled', tabsUnloaderPrefListener);
      Services.prefs.removeObserver('zen.glance.activation-method', tabsUnloaderPrefListener);
      Services.prefs.removeObserver('zen.workspaces.separate-essentials', tabsUnloaderPrefListener);
    });
  },
};

const ZEN_CKS_CLASS_BASE = 'zenCKSOption';
const ZEN_CKS_INPUT_FIELD_CLASS = `${ZEN_CKS_CLASS_BASE}-input`;
const ZEN_CKS_LABEL_CLASS = `${ZEN_CKS_CLASS_BASE}-label`;
const ZEN_CKS_WRAPPER_ID = `${ZEN_CKS_CLASS_BASE}-wrapper`;
const ZEN_CKS_GROUP_PREFIX = `${ZEN_CKS_CLASS_BASE}-group`;
const KEYBIND_ATTRIBUTE_KEY = 'key';

var zenMissingKeyboardShortcutL10n = {
  key_quickRestart: 'zen-key-quick-restart',
  key_delete: 'zen-key-delete',
  goBackKb: 'zen-key-go-back',
  goForwardKb: 'zen-key-go-forward',
  key_enterFullScreen: 'zen-key-enter-full-screen',
  key_exitFullScreen: 'zen-key-exit-full-screen',
  key_aboutProcesses: 'zen-key-about-processes',
  key_stop: 'zen-key-stop',
  key_sanitize: 'zen-key-sanitize',
  key_wrCaptureCmd: 'zen-key-wr-capture-cmd',
  key_wrToggleCaptureSequenceCmd: 'zen-key-wr-toggle-capture-sequence-cmd',
  key_undoCloseWindow: 'zen-key-undo-close-window',

  key_selectTab1: 'zen-key-select-tab-1',
  key_selectTab2: 'zen-key-select-tab-2',
  key_selectTab3: 'zen-key-select-tab-3',
  key_selectTab4: 'zen-key-select-tab-4',
  key_selectTab5: 'zen-key-select-tab-5',
  key_selectTab6: 'zen-key-select-tab-6',
  key_selectTab7: 'zen-key-select-tab-7',
  key_selectTab8: 'zen-key-select-tab-8',
  key_selectLastTab: 'zen-key-select-tab-last',

  key_showAllTabs: 'zen-key-show-all-tabs',
  key_gotoHistory: 'zen-key-goto-history',

  goHome: 'zen-key-go-home',
  key_redo: 'zen-key-redo',

  key_inspectorMac: 'zen-key-inspector-mac',

  // Devtools
  key_toggleToolbox: 'zen-devtools-toggle-shortcut',
  key_browserToolbox: 'zen-devtools-toggle-browser-toolbox-shortcut',
  key_browserConsole: 'zen-devtools-toggle-browser-console-shortcut',
  key_responsiveDesignMode: 'zen-devtools-toggle-responsive-design-mode-shortcut',
  key_inspector: 'zen-devtools-toggle-inspector-shortcut',
  key_webconsole: 'zen-devtools-toggle-web-console-shortcut',
  key_jsdebugger: 'zen-devtools-toggle-js-debugger-shortcut',
  key_netmonitor: 'zen-devtools-toggle-net-monitor-shortcut',
  key_styleeditor: 'zen-devtools-toggle-style-editor-shortcut',
  key_performance: 'zen-devtools-toggle-performance-shortcut',
  key_storage: 'zen-devtools-toggle-storage-shortcut',
  key_dom: 'zen-devtools-toggle-dom-shortcut',
  key_accessibility: 'zen-devtools-toggle-accessibility-shortcut',
};

var zenIgnoreKeyboardShortcutL10n = [
  'zen-full-zoom-reduce-shortcut-alt-b',
  'zen-full-zoom-reduce-shortcut-alt-a',
];

var gZenCKSSettings = {
  async init() {
    await this._initializeCKS();
    if (this.__hasInitialized) return;
    this.__hasInitialized = true;
    this._currentActionID = null;
    this._initializeEvents();
    window.addEventListener('unload', () => {
      this.__hasInitialized = false;
      document.getElementById(ZEN_CKS_WRAPPER_ID).innerHTML = '';
    });
  },

  _initializeEvents() {
    const resetAllListener = this.resetAllShortcuts.bind(this);
    const handleKeyDown = this._handleKeyDown.bind(this);
    window.addEventListener('keydown', handleKeyDown);
    const button = document.getElementById('zenCKSResetButton');
    button.addEventListener('click', resetAllListener);
    window.addEventListener('unload', () => {
      window.removeEventListener('keydown', handleKeyDown);
      button.removeEventListener('click', resetAllListener);
    });
  },

  async resetAllShortcuts() {
    let buttonIndex = await confirmRestartPrompt(true, 1, true, false);
    if (buttonIndex == CONFIRM_RESTART_PROMPT_RESTART_NOW) {
      await gZenKeyboardShortcutsManager.resetAllShortcuts();
      Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
    }
  },

  async _initializeCKS() {
    let wrapper = document.getElementById(ZEN_CKS_WRAPPER_ID);
    wrapper.innerHTML = '';

    let shortcuts = await gZenKeyboardShortcutsManager.getModifiableShortcuts();

    if (!shortcuts) {
      throw Error('No shortcuts defined!');
    }

    // Generate section per each group
    for (let group of VALID_SHORTCUT_GROUPS) {
      let groupClass = `${ZEN_CKS_GROUP_PREFIX}-${group}`;
      if (!wrapper.querySelector(`[data-group="${groupClass}"]`)) {
        let groupElem = document.createElement('h2');
        groupElem.setAttribute('data-group', groupClass);
        document.l10n.setAttributes(groupElem, groupClass);
        wrapper.appendChild(groupElem);
      }
    }

    for (let shortcut of shortcuts) {
      const keyID = shortcut.getID();
      const action = shortcut.getAction();
      const l10nID = shortcut.getL10NID();
      const group = shortcut.getGroup();
      const keyInString = shortcut.toUserString();

      const labelValue = zenMissingKeyboardShortcutL10n[keyID] ?? l10nID;

      if (zenIgnoreKeyboardShortcutL10n.includes(labelValue)) {
        continue;
      }

      let fragment = window.MozXULElement.parseXULToFragment(`
        <hbox class="${ZEN_CKS_CLASS_BASE}">
          <label class="${ZEN_CKS_LABEL_CLASS}" for="${ZEN_CKS_CLASS_BASE}-${keyID}"></label>
          <vbox flex="1">
            <html:input readonly="1" class="${ZEN_CKS_INPUT_FIELD_CLASS}" id="${ZEN_CKS_INPUT_FIELD_CLASS}-${keyID}" />
          </vbox>
        </hbox>
      `);

      const label = fragment.querySelector(`.${ZEN_CKS_LABEL_CLASS}`);
      if (!labelValue) {
        label.textContent = action; // Just in case
      } else {
        document.l10n.setAttributes(label, labelValue);
      }

      let input = fragment.querySelector(`.${ZEN_CKS_INPUT_FIELD_CLASS}`);
      if (keyInString && !shortcut.isEmpty()) {
        input.value = keyInString;
      } else {
        this._resetShortcut(input);
      }

      input.setAttribute(KEYBIND_ATTRIBUTE_KEY, keyID);
      input.setAttribute('data-group', group);
      input.setAttribute('data-id', keyID);

      input.addEventListener('focus', (event) => {
        const value = event.target.getAttribute(KEYBIND_ATTRIBUTE_KEY);
        this._currentActionID = event.target.getAttribute('data-id');
        event.target.classList.add(`${ZEN_CKS_INPUT_FIELD_CLASS}-editing`);
        this._hasSafed = true;
      });

      input.addEventListener('editDone', (event) => {
        const target = event.target;
        target.classList.add(`${ZEN_CKS_INPUT_FIELD_CLASS}-editing`);
      });

      input.addEventListener('blur', (event) => {
        const target = event.target;
        target.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-editing`);
        if (!this._hasSafed) {
          target.classList.add(`${ZEN_CKS_INPUT_FIELD_CLASS}-unsafed`);
          if (!target.nextElementSibling) {
            target.after(
              window.MozXULElement.parseXULToFragment(`
              <label class="${ZEN_CKS_CLASS_BASE}-unsafed" data-l10n-id="zen-key-unsaved"></label>
            `)
            );
            target.value = 'Not set';
          }
        } else {
          target.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-unsafed`);
          const sibling = target.nextElementSibling;
          if (sibling && sibling.classList.contains(`${ZEN_CKS_CLASS_BASE}-unsafed`)) {
            sibling.remove();
          }
        }
        if (target.classList.contains(`${ZEN_CKS_INPUT_FIELD_CLASS}-not-set`)) {
          target.label = 'Not set';
        }
      });

      const groupElem = wrapper.querySelector(`[data-group="${ZEN_CKS_GROUP_PREFIX}-${group}"]`);
      groupElem.after(fragment);
    }
  },

  async _resetShortcut(input) {
    input.value = 'Not set';
    input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-invalid`);
    input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-editing`);
    input.classList.add(`${ZEN_CKS_INPUT_FIELD_CLASS}-not-set`);

    if (this._currentActionID) {
      this._editDone();
      await gZenKeyboardShortcutsManager.setShortcut(this._currentActionID, null, null);
    }
  },

  _editDone(shortcut, modifiers) {
    // Check if we have a valid key
    if (!shortcut || !modifiers) {
      return;
    }
    gZenKeyboardShortcutsManager.setShortcut(this._currentActionID, shortcut, modifiers);
    this._currentActionID = null;
  },

  //TODO Check for duplicates
  async _handleKeyDown(event) {
    if (!this._currentActionID || document.hidden) {
      return;
    }

    event.preventDefault();

    let input = document.querySelector(
      `.${ZEN_CKS_INPUT_FIELD_CLASS}[${KEYBIND_ATTRIBUTE_KEY}="${this._currentActionID}"]`
    );
    const modifiers = new nsKeyShortcutModifiers(
      event.ctrlKey,
      event.altKey,
      event.shiftKey,
      event.metaKey,
      false
    );
    const modifiersActive = modifiers.areAnyActive();

    input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-not-set`);

    // First, try to read the *physical* key via event.code.
    // If event.code is like "KeyS", "KeyA", ..., strip off "Key" → "S".
    // Otherwise, fall back to event.key (e.g. "F5", "Enter", etc.).
    let shortcut;
    if (event.code && event.code.startsWith('Key')) shortcut = event.code.slice(3);
    else shortcut = event.key;

    shortcut = shortcut.replace(/Ctrl|Control|Shift|Alt|Option|Cmd|Meta/, ''); // Remove all modifiers

    if (shortcut == 'Tab' && !modifiersActive) {
      input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-not-set`);
      input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-editing`);
      this._latestValidKey = null;
      return;
    } else if (shortcut == 'Escape' && !modifiersActive) {
      const hasConflicts = gZenKeyboardShortcutsManager.checkForConflicts(
        this._latestValidKey ? this._latestValidKey : shortcut,
        this._latestModifier ? this._latestModifier : modifiers,
        this._currentActionID
      );

      if (!this._latestValidKey && !this._latestModifier) {
      } else if (!this._latestValidKey || hasConflicts) {
        if (!input.classList.contains(`${ZEN_CKS_INPUT_FIELD_CLASS}-invalid`)) {
          input.classList.add(`${ZEN_CKS_INPUT_FIELD_CLASS}-invalid`);
        }
        input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-unsafed`);
        if (hasConflicts && !input.nextElementSibling) {
          input.after(
            window.MozXULElement.parseXULToFragment(`
            <label class="${ZEN_CKS_CLASS_BASE}-conflict" data-l10n-id="zen-key-conflict"></label>
          `)
          );
        }
      } else {
        input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-editing`);

        this._editDone(this._latestValidKey, this._latestModifier);
        if (this.name == 'Not set') {
          input.classList.add(`${ZEN_CKS_INPUT_FIELD_CLASS}-not-set`);
        }
        this._latestValidKey = null;
        this._latestModifier = null;
        input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-invalid`);
        input.classList.add(`${ZEN_CKS_INPUT_FIELD_CLASS}-valid`);
        setTimeout(() => {
          input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-valid`);
        }, 1000);
        const sibling = input.nextElementSibling;
        if (sibling && sibling.classList.contains(`${ZEN_CKS_CLASS_BASE}-conflict`)) {
          sibling.remove();
        }
      }
      this._hasSafed = true;
      input.blur();
      this._currentActionID = null;
      return;
    } else if (shortcut == 'Backspace' && !modifiersActive) {
      this._resetShortcut(input);
      this._latestValidKey = null;
      this._latestModifier = null;
      this._hasSafed = true;
      const sibling = input.nextElementSibling;
      if (sibling && sibling.classList.contains(`${ZEN_CKS_CLASS_BASE}-conflict`)) {
        sibling.remove();
      }
      return;
    }

    this._latestModifier = modifiers;
    this._hasSafed = false;
    input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-invalid`);
    input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-not-set`);
    input.value = modifiers.toUserString() + shortcut;
    this._latestValidKey = shortcut;
  },
};

Preferences.addAll([
  {
    id: 'zen.view.compact.hide-toolbar',
    type: 'bool',
    default: false,
  },
  {
    id: 'zen.view.compact.toolbar-flash-popup',
    type: 'bool',
    default: true,
  },
  {
    id: 'zen.workspaces.hide-default-container-indicator',
    type: 'bool',
    default: true,
  },
  {
    id: 'zen.tab-unloader.enabled',
    type: 'bool',
    default: true,
  },
  {
    id: 'zen.tab-unloader.timeout-minutes',
    type: 'int',
    default: 10,
  },
  {
    id: 'zen.pinned-tab-manager.restore-pinned-tabs-to-pinned-url',
    type: 'bool',
    default: true,
  },
  {
    id: 'zen.pinned-tab-manager.close-shortcut-behavior',
    type: 'string',
    default: 'switch',
  },
  {
    id: 'zen.workspaces.force-container-workspace',
    type: 'bool',
    default: true,
  },
  {
    id: 'zen.workspaces.open-new-tab-if-last-unpinned-tab-is-closed',
    type: 'bool',
    default: true,
  },
  {
    id: 'zen.tabs.show-newtab-under',
    type: 'bool',
    default: false,
  },
  {
    id: 'zen.glance.activation-method',
    type: 'string',
    default: 'ctrl',
  },
  {
    id: 'zen.glance.enabled',
    type: 'bool',
    default: true,
  },
  {
    id: 'zen.view.compact.color-toolbar',
    type: 'bool',
    default: true,
  },
  {
    id: 'zen.urlbar.behavior',
    type: 'string',
    default: 'float',
  },
  {
    id: 'zen.view.compact.color-sidebar',
    type: 'bool',
    default: true,
  },
  {
    id: 'zen.workspaces.separate-essentials',
    type: 'bool',
    default: false,
  },
  {
    id: 'zen.tabs.show-newtab-vertical',
    type: 'bool',
    default: true,
  },
  {
    id: 'zen.view.show-newtab-button-border-top',
    type: 'bool',
    default: false,
  },
  {
    id: 'zen.view.show-newtab-button-top',
    type: 'bool',
    default: true,
  },
  {
    id: 'media.videocontrols.picture-in-picture.enabled',
    type: 'bool',
    default: true,
  },
  {
    id: 'zen.workspaces.continue-where-left-off',
    type: 'bool',
    default: false,
  },
  {
    id: 'zen.mods.auto-update',
    type: 'bool',
    default: true,
  },
]);
