/* 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 {
  classMap,
  html,
  ifDefined,
  styleMap,
  when,
} from "chrome://global/content/vendor/lit.all.mjs";
import {
  FxviewTabListBase,
  FxviewTabRowBase,
} from "chrome://browser/content/firefoxview/fxview-tab-list.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://global/content/elements/moz-button.mjs";
const lazy = {};
let XPCOMUtils;
XPCOMUtils = ChromeUtils.importESModule(
  "resource://gre/modules/XPCOMUtils.sys.mjs"
).XPCOMUtils;
XPCOMUtils.defineLazyPreferenceGetter(
  lazy,
  "virtualListEnabledPref",
  "browser.firefox-view.virtual-list.enabled"
);
/**
 * A list of clickable tab items
 *
 * @property {boolean} pinnedTabsGridView - Whether to show pinned tabs in a grid view
 */
export class OpenTabsTabList extends FxviewTabListBase {
  constructor() {
    super();
    this.pinnedTabsGridView = false;
    this.pinnedTabs = [];
    this.unpinnedTabs = [];
  }
  static properties = {
    pinnedTabsGridView: { type: Boolean },
  };
  static queries = {
    ...FxviewTabListBase.queries,
    rowEls: {
      all: "opentabs-tab-row",
    },
  };
  willUpdate(changes) {
    this.activeIndex = Math.min(
      Math.max(this.activeIndex, 0),
      this.tabItems.length - 1
    );
    if (changes.has("dateTimeFormat") || changes.has("updatesPaused")) {
      this.clearIntervalTimer();
      if (!this.updatesPaused && this.dateTimeFormat == "relative") {
        this.startIntervalTimer();
        this.onIntervalUpdate();
      }
    }
    // Move pinned tabs to the beginning of the list
    if (this.pinnedTabsGridView) {
      // Can set maxTabsLength to -1 to have no max
      this.unpinnedTabs = this.tabItems.filter(
        tab => !tab.indicators.includes("pinned")
      );
      this.pinnedTabs = this.tabItems.filter(tab =>
        tab.indicators.includes("pinned")
      );
      if (this.maxTabsLength > 0) {
        this.unpinnedTabs = this.unpinnedTabs.slice(0, this.maxTabsLength);
      }
      this.tabItems = [...this.pinnedTabs, ...this.unpinnedTabs];
    } else if (this.maxTabsLength > 0) {
      this.tabItems = this.tabItems.slice(0, this.maxTabsLength);
    }
  }
  /**
   * Focuses the expected element (either the link or button) within fxview-tab-row
   * The currently focused/active element ID within a row is stored in this.currentActiveElementId
   */
  handleFocusElementInRow(e) {
    let fxviewTabRow = e.target;
    if (e.code == "ArrowUp") {
      // Focus either the link or button of the previous row based on this.currentActiveElementId
      e.preventDefault();
      if (
        (this.pinnedTabsGridView &&
          this.activeIndex >= this.pinnedTabs.length) ||
        !this.pinnedTabsGridView
      ) {
        this.focusPrevRow();
      }
    } else if (e.code == "ArrowDown") {
      // Focus either the link or button of the next row based on this.currentActiveElementId
      e.preventDefault();
      if (
        this.pinnedTabsGridView &&
        this.activeIndex < this.pinnedTabs.length
      ) {
        this.focusIndex(this.pinnedTabs.length);
      } else {
        this.focusNextRow();
      }
    } else if (e.code == "ArrowRight") {
      // Focus either the link or the button in the current row and
      // set this.currentActiveElementId to that element's ID
      e.preventDefault();
      if (document.dir == "rtl") {
        fxviewTabRow.moveFocusLeft();
      } else {
        fxviewTabRow.moveFocusRight();
      }
    } else if (e.code == "ArrowLeft") {
      // Focus either the link or the button in the current row and
      // set this.currentActiveElementId to that element's ID
      e.preventDefault();
      if (document.dir == "rtl") {
        fxviewTabRow.moveFocusRight();
      } else {
        fxviewTabRow.moveFocusLeft();
      }
    }
  }
  async focusIndex(index) {
    // Focus link or button of item
    if (
      ((this.pinnedTabsGridView && index > this.pinnedTabs.length) ||
        !this.pinnedTabsGridView) &&
      lazy.virtualListEnabledPref
    ) {
      let row = this.rootVirtualListEl.getItem(index - this.pinnedTabs.length);
      if (!row) {
        return;
      }
      let subList = this.rootVirtualListEl.getSubListForItem(
        index - this.pinnedTabs.length
      );
      if (!subList) {
        return;
      }
      this.activeIndex = index;
      // In Bug 1866845, these manual updates to the sublists should be removed
      // and scrollIntoView() should also be iterated on so that we aren't constantly
      // moving the focused item to the center of the viewport
      for (const sublist of Array.from(this.rootVirtualListEl.children)) {
        await sublist.requestUpdate();
        await sublist.updateComplete;
      }
      row.scrollIntoView({ block: "center" });
      row.focus();
    } else if (index >= 0 && index < this.rowEls?.length) {
      this.rowEls[index].focus();
      this.activeIndex = index;
    }
  }
  #getTabListWrapperClasses() {
    let wrapperClasses = ["fxview-tab-list"];
    let tabsToCheck = this.pinnedTabsGridView
      ? this.unpinnedTabs
      : this.tabItems;
    if (tabsToCheck.some(tab => tab.containerObj)) {
      wrapperClasses.push(`hasContainerTab`);
    }
    return wrapperClasses;
  }
  itemTemplate = (tabItem, i) => {
    let time;
    if (tabItem.time || tabItem.closedAt) {
      let stringTime = (tabItem.time || tabItem.closedAt).toString();
      // Different APIs return time in different units, so we use
      // the length to decide if it's milliseconds or nanoseconds.
      if (stringTime.length === 16) {
        time = (tabItem.time || tabItem.closedAt) / 1000;
      } else {
        time = tabItem.time || tabItem.closedAt;
      }
    }
    return html`