/* 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/. */
const lazy = {};
import {
  classMap,
  html,
  ifDefined,
  when,
  nothing,
} from "chrome://global/content/vendor/lit.all.mjs";
import { navigateToLink } from "chrome://browser/content/firefoxview/helpers.mjs";
import { SidebarPage } from "./sidebar-page.mjs";
ChromeUtils.defineESModuleGetters(lazy, {
  HistoryController: "resource:///modules/HistoryController.sys.mjs",
  Sanitizer: "resource:///modules/Sanitizer.sys.mjs",
});
const NEVER_REMEMBER_HISTORY_PREF = "browser.privatebrowsing.autostart";
const DAYS_EXPANDED_INITIALLY = 2;
export class SidebarHistory extends SidebarPage {
  static queries = {
    cards: { all: "moz-card" },
    emptyState: "fxview-empty-state",
    lists: { all: "sidebar-tab-list" },
    menuButton: ".menu-button",
    searchTextbox: "moz-input-search",
  };
  constructor() {
    super();
    this.handlePopupEvent = this.handlePopupEvent.bind(this);
    this.controller = new lazy.HistoryController(this, {
      component: "sidebar",
    });
    this.selectedLists = new Set();
  }
  connectedCallback() {
    super.connectedCallback();
    const { document: doc } = this.topWindow;
    this._menu = doc.getElementById("sidebar-history-menu");
    this._menuSortByDate = doc.getElementById("sidebar-history-sort-by-date");
    this._menuSortBySite = doc.getElementById("sidebar-history-sort-by-site");
    this._menuSortByDateSite = doc.getElementById(
      "sidebar-history-sort-by-date-and-site"
    );
    this._menuSortByLastVisited = doc.getElementById(
      "sidebar-history-sort-by-last-visited"
    );
    this._menu.addEventListener("command", this);
    this._menu.addEventListener("popuphidden", this.handlePopupEvent);
    this.addEventListener("update-selection", this);
    this.addEventListener("clear-selection", this);
    this._contextMenu.addEventListener("popupshowing", this);
    this.addContextMenuListeners();
    this.addSidebarFocusedListeners();
    this.controller.updateCache();
  }
  disconnectedCallback() {
    super.disconnectedCallback();
    this._menu.removeEventListener("command", this);
    this._menu.removeEventListener("popuphidden", this.handlePopupEvent);
    this.removeEventListener("update-selection", this);
    this.removeEventListener("clear-selection", this);
    this._contextMenu.removeEventListener("popupshowing", this);
    this.removeContextMenuListeners();
    this.removeSidebarFocusedListeners();
  }
  handleEvent(e) {
    switch (e.type) {
      case "update-selection":
        this.selectedLists.add(e.originalTarget);
        break;
      case "clear-selection":
        this.selectedLists.delete(e.originalTarget);
        this.#clearSelection();
        break;
      case "popupshowing":
        this.updateContextMenu();
        break;
      default:
        super.handleEvent(e);
    }
  }
  get isMultipleRowsSelected() {
    return !!this.selectedLists.size;
  }
  /**
   * Only show multiselect commands when multiple items are selected.
   */
  updateContextMenu() {
    for (const child of this._contextMenu.children) {
      const isMultiSelectCommand = child.classList.contains(
        "sidebar-history-multiselect-command"
      );
      if (this.isMultipleRowsSelected) {
        child.hidden = !isMultiSelectCommand;
      } else {
        child.hidden = isMultiSelectCommand;
      }
    }
  }
  handleContextMenuEvent(e) {
    this.triggerNode = this.findTriggerNode(e, "sidebar-tab-row");
    if (!this.triggerNode) {
      e.preventDefault();
    }
  }
  handleCommandEvent(e) {
    switch (e.target.id) {
      case "sidebar-history-sort-by-date":
        this.controller.onChangeSortOption(e, "date");
        break;
      case "sidebar-history-sort-by-site":
        this.controller.onChangeSortOption(e, "site");
        break;
      case "sidebar-history-sort-by-date-and-site":
        this.controller.onChangeSortOption(e, "datesite");
        break;
      case "sidebar-history-sort-by-last-visited":
        this.controller.onChangeSortOption(e, "lastvisited");
        break;
      case "sidebar-history-clear":
        lazy.Sanitizer.showUI(this.topWindow);
        break;
      case "sidebar-history-context-delete-page":
        this.controller.deleteFromHistory().catch(console.error);
        break;
      case "sidebar-history-context-delete-pages":
        this.controller.deleteMultipleFromHistory().catch(console.error);
        break;
      default:
        super.handleCommandEvent(e);
        break;
    }
  }
  // We should let moz-button handle this, see bug 1875374.
  handlePopupEvent(e) {
    if (e.type == "popuphidden") {
      this.menuButton.setAttribute("aria-expanded", false);
    }
  }
  handleSidebarFocusedEvent() {
    this.searchTextbox?.focus();
  }
  onPrimaryAction(e) {
    if (this.isMultipleRowsSelected) {
      // Avoid opening multiple links at once.
      return;
    }
    navigateToLink(e);
    this.#clearSelection();
  }
  onSecondaryAction(e) {
    this.triggerNode = e.detail.item;
    this.controller.deleteFromHistory().catch(console.error);
  }
  handleCardKeydown(e) {
    if (e.originalTarget != e.target.summaryEl) {
      return;
    }
    let nextSibling = e.target.nextElementSibling;
    let prevSibling = e.target.previousElementSibling;
    let focusedRow = null;
    switch (e.code) {
      case "Tab":
        if (prevSibling.localName == "moz-card") {
          e.preventDefault();
        }
        break;
      case "ArrowUp":
        if (!prevSibling || prevSibling.localName !== "moz-card") {
          const { classList, parentElement: dateCard } = e.target;
          if (classList.contains("nested-card")) {
            // Going up from the first site card. Focus the date header.
            dateCard.summaryEl.focus();
          }
          break;
        }
        if (prevSibling.expanded) {
          let innerElement = prevSibling.contentSlotEl.assignedElements()[0];
          if (innerElement.classList.contains("nested-card")) {
            // Going up from a date header. Focus the last site card from the
            // date card above this one.
            const prevSite = prevSibling.lastElementChild;
            if (prevSite.expanded) {
              const prevTabList = prevSite.contentSlotEl.assignedElements()[0];
              focusedRow = prevTabList.rowEls[prevTabList.rowEls.length - 1];
              focusedRow.focus();
            } else {
              prevSite.summaryEl.focus();
            }
          } else {
            // Not sorted by Date & Site, innerElement is a SidebarTabList.
            focusedRow = innerElement.rowEls[innerElement.rowEls.length - 1];
            focusedRow.focus();
          }
        } else {
          prevSibling.summaryEl.focus();
        }
        break;
      case "ArrowDown":
        if (e.target.expanded) {
          let innerElement = e.target.contentSlotEl.assignedElements()[0];
          if (innerElement.classList.contains("nested-card")) {
            // Going down from a date header. Focus the first site card.
            innerElement.summaryEl.focus();
          } else {
            // Not sorted by Date & Site, innerElement is a SidebarTabList.
            focusedRow = innerElement.rowEls[0];
            focusedRow.focus();
          }
        } else if (nextSibling && nextSibling.localName == "moz-card") {
          nextSibling.summaryEl.focus();
        } else if (e.target.classList.contains("last-card")) {
          // Going down from the last site card. Focus the next date header.
          const dateCard = e.target.parentElement;
          const nextDate = dateCard.nextElementSibling;
          nextDate?.summaryEl.focus();
        }
        break;
      case "ArrowLeft":
        e.target.expanded = false;
        break;
      case "ArrowRight":
        e.target.expanded = true;
        break;
    }
    this.#updateSelection(e, focusedRow);
  }
  /**
   * When a row is focused while the shift key is held down, add it to the
   * selection. If shift key was not held down, clear the selection.
   *
   * @param {KeyboardEvent} event
   * @param {Element} rowEl
   */
  #updateSelection(event, rowEl) {
    if (event.code !== "ArrowUp" && event.code !== "ArrowDown") {
      return;
    }
    if (!event.shiftKey) {
      this.#clearSelection();
      return;
    }
    if (rowEl != null) {
      const listForRow = rowEl.getRootNode().host;
      listForRow.selectedGuids.add(rowEl.guid);
      listForRow.requestVirtualListUpdate();
      this.selectedLists.add(listForRow);
    }
  }
  /**
   * Clear the selection from all lists.
   */
  #clearSelection() {
    for (const list of this.selectedLists) {
      list.clearSelection();
    }
    this.selectedLists.clear();
  }
  /**
   * The template to use for cards-container.
   */
  get cardsTemplate() {
    if (this.controller.isHistoryPending) {
      // don't render cards until initial history visits entries are available
      return "";
    } else if (this.controller.searchResults) {
      return this.#searchResultsTemplate();
    } else if (!this.controller.isHistoryEmpty) {
      return this.#historyCardsTemplate();
    }
    return this.#emptyMessageTemplate();
  }
  #historyCardsTemplate() {
    const { historyVisits } = this.controller;
    switch (this.controller.sortOption) {
      case "date":
        return historyVisits.map(({ l10nId, items }, i) =>
          this.#dateCardTemplate(l10nId, i, items)
        );
      case "site":
        return historyVisits.map(({ domain, items }, i) =>
          this.#siteCardTemplate(domain, i, items)
        );
      case "datesite":
        return historyVisits.map(({ l10nId, items }, i) =>
          this.#dateCardTemplate(l10nId, i, items, true)
        );
      case "lastvisited":
        return historyVisits.map(
          ({ items }) =>
            html`