/* 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 = {};
ChromeUtils.defineESModuleGetters(lazy, {
  SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
});
import {
  formatURIForDisplay,
  convertTimestamp,
  getImageUrl,
  onToggleContainer,
  NOW_THRESHOLD_MS,
} from "./helpers.mjs";
import {
  html,
  ifDefined,
  styleMap,
} from "chrome://global/content/vendor/lit.all.mjs";
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
const { XPCOMUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/XPCOMUtils.sys.mjs"
);
const SS_NOTIFY_CLOSED_OBJECTS_CHANGED = "sessionstore-closed-objects-changed";
const SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH = "sessionstore-browser-shutdown-flush";
const UI_OPEN_STATE =
  "browser.tabs.firefox-view.ui-state.recently-closed-tabs.open";
function getWindow() {
  return window.browsingContext.embedderWindowGlobal.browsingContext.window;
}
class RecentlyClosedTabsList extends MozLitElement {
  constructor() {
    super();
    this.maxTabsLength = 25;
    this.recentlyClosedTabs = [];
    this.lastFocusedIndex = -1;
    // The recency timestamp update period is stored in a pref to allow tests to easily change it
    XPCOMUtils.defineLazyPreferenceGetter(
      lazy,
      "timeMsPref",
      "browser.tabs.firefox-view.updateTimeMs",
      NOW_THRESHOLD_MS,
      timeMsPref => {
        clearInterval(this.intervalID);
        this.intervalID = setInterval(() => this.requestUpdate(), timeMsPref);
        this.requestUpdate();
      }
    );
  }
  createRenderRoot() {
    return this;
  }
  static queries = {
    tabsList: "ol",
    timeElements: { all: "span.closed-tab-li-time" },
  };
  get fluentStrings() {
    if (!this._fluentStrings) {
      this._fluentStrings = new Localization(["browser/firefoxView.ftl"], true);
    }
    return this._fluentStrings;
  }
  connectedCallback() {
    super.connectedCallback();
    this.intervalID = setInterval(() => this.requestUpdate(), lazy.timeMsPref);
  }
  disconnectedCallback() {
    clearInterval(this.intervalID);
  }
  getTabStateValue(tab, key) {
    let value = "";
    const tabEntries = tab.state.entries;
    const activeIndex = tab.state.index - 1;
    if (activeIndex >= 0 && tabEntries[activeIndex]) {
      value = tabEntries[activeIndex][key];
    }
    return value;
  }
  openTabAndUpdate(event) {
    if (
      (event.type == "click" && !event.altKey) ||
      (event.type == "keydown" && event.code == "Enter") ||
      (event.type == "keydown" && event.code == "Space")
    ) {
      const item = event.target.closest(".closed-tab-li");
      // only used for telemetry
      const position = [...this.tabsList.children].indexOf(item) + 1;
      const closedId = item.dataset.tabid;
      lazy.SessionStore.undoCloseById(closedId);
      // record telemetry
      let tabClosedAt = parseInt(
        item.querySelector(".closed-tab-li-time").getAttribute("data-timestamp")
      );
      let now = Date.now();
      let deltaSeconds = (now - tabClosedAt) / 1000;
      Services.telemetry.recordEvent(
        "firefoxview",
        "recently_closed",
        "tabs",
        null,
        {
          position: position.toString(),
          delta: deltaSeconds.toString(),
        }
      );
    }
  }
  dismissTabAndUpdate(event) {
    event.preventDefault();
    const item = event.target.closest(".closed-tab-li");
    let recentlyClosedList = lazy.SessionStore.getClosedTabData(getWindow());
    let closedTabIndex = recentlyClosedList.findIndex(closedTab => {
      return closedTab.closedId === parseInt(item.dataset.tabid, 10);
    });
    if (closedTabIndex < 0) {
      // Tab not found in recently closed list
      return;
    }
    lazy.SessionStore.forgetClosedTab(getWindow(), closedTabIndex);
    // record telemetry
    let tabClosedAt = parseInt(
      item.querySelector(".closed-tab-li-time").dataset.timestamp
    );
    let now = Date.now();
    let deltaSeconds = (now - tabClosedAt) / 1000;
    Services.telemetry.recordEvent(
      "firefoxview",
      "dismiss_closed_tab",
      "tabs",
      null,
      {
        delta: deltaSeconds.toString(),
      }
    );
  }
  updateRecentlyClosedTabs() {
    let recentlyClosedTabsData = lazy.SessionStore.getClosedTabData(
      getWindow()
    );
    this.recentlyClosedTabs = recentlyClosedTabsData.slice(
      0,
      this.maxTabsLength
    );
    this.requestUpdate();
  }
  render() {
    let { recentlyClosedTabs } = this;
    let closedTabsContainer = document.getElementById(
      "recently-closed-tabs-container"
    );
    if (!recentlyClosedTabs.length) {
      // Show empty message if no recently closed tabs
      closedTabsContainer.toggleContainerStyleForEmptyMsg(true);
      return html`
        ${this.emptyMessageTemplate()}
      `;
    }
    closedTabsContainer.toggleContainerStyleForEmptyMsg(false);
    return html`
      
        ${recentlyClosedTabs.map((tab, i) =>
          this.recentlyClosedTabTemplate(tab, !i)
        )}
      
`;
  }
  willUpdate() {
    if (this.tabsList && this.tabsList.contains(document.activeElement)) {
      let activeLi = document.activeElement.closest(".closed-tab-li");
      this.lastFocusedIndex = [...this.tabsList.children].indexOf(activeLi);
    } else {
      this.lastFocusedIndex = -1;
    }
  }
  updated() {
    let focusRestored = false;
    if (
      this.lastFocusedIndex >= 0 &&
      (!this.tabsList || this.lastFocusedIndex >= this.tabsList.children.length)
    ) {
      if (this.tabsList) {
        let items = [...this.tabsList.children];
        let newFocusIndex = items.length - 1;
        let newFocus = items[newFocusIndex];
        if (newFocus) {
          focusRestored = true;
          newFocus.querySelector(".closed-tab-li-main").focus();
        }
      }
      if (!focusRestored) {
        document.getElementById("recently-closed-tabs-header-section").focus();
      }
    }
    this.lastFocusedIndex = -1;
  }
  emptyMessageTemplate() {
    return html`
      
    `;
  }
  recentlyClosedTabTemplate(tab, primary) {
    const targetURI = this.getTabStateValue(tab, "url");
    const convertedTime = convertTimestamp(
      tab.closedAt,
      this.fluentStrings,
      lazy.timeMsPref
    );
    return html`
      
         this.openTabAndUpdate(e)}
          @keydown=${e => this.openTabAndUpdate(e)}
        >
          
          
            ${tab.title}
          
          
            ${formatURIForDisplay(targetURI)}
          
          
            ${convertedTime}
          
        
        
      
    `;
  }
  // Update the URL for a new or previously-populated list item.
  // This is needed because when tabs get closed we don't necessarily
  // have all the requisite information for them immediately.
  updateURLForListItem(li, targetURI) {
    li.dataset.targetURI = targetURI;
    let urlElement = li.querySelector(".closed-tab-li-url");
    document.l10n.setAttributes(
      urlElement,
      "firefoxview-tabs-list-tab-button",
      {
        targetURI,
      }
    );
    if (targetURI) {
      urlElement.textContent = formatURIForDisplay(targetURI);
      urlElement.title = targetURI;
    } else {
      urlElement.textContent = urlElement.title = "";
    }
  }
}
customElements.define("recently-closed-tabs-list", RecentlyClosedTabsList);
class RecentlyClosedTabsContainer extends HTMLDetailsElement {
  constructor() {
    super();
    this.observerAdded = false;
    this.boundObserve = (...args) => this.observe(...args);
  }
  connectedCallback() {
    this.noTabsElement = this.querySelector(
      "#recently-closed-tabs-placeholder"
    );
    this.list = this.querySelector("recently-closed-tabs-list");
    this.collapsibleContainer = this.querySelector(
      "#collapsible-tabs-container"
    );
    this.addEventListener("toggle", this);
    getWindow().gBrowser.tabContainer.addEventListener("TabSelect", this);
    this.open = Services.prefs.getBoolPref(UI_OPEN_STATE, true);
  }
  cleanup() {
    getWindow().gBrowser.tabContainer.removeEventListener("TabSelect", this);
    this.removeObserversIfNeeded();
  }
  addObserversIfNeeded() {
    if (!this.observerAdded) {
      Services.obs.addObserver(
        this.boundObserve,
        SS_NOTIFY_CLOSED_OBJECTS_CHANGED
      );
      Services.obs.addObserver(
        this.boundObserve,
        SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH
      );
      this.observerAdded = true;
    }
  }
  removeObserversIfNeeded() {
    if (this.observerAdded) {
      Services.obs.removeObserver(
        this.boundObserve,
        SS_NOTIFY_CLOSED_OBJECTS_CHANGED
      );
      Services.obs.removeObserver(
        this.boundObserve,
        SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH
      );
      this.observerAdded = false;
    }
  }
  // we observe when a tab closes but since this notification fires more frequently and on
  // all windows, we remove the observer when another tab is selected; we check for changes
  // to the session store once the user return to this tab.
  handleObservers(contentDocument) {
    if (contentDocument?.URL == "about:firefoxview") {
      this.addObserversIfNeeded();
      this.list.updateRecentlyClosedTabs();
    } else {
      this.removeObserversIfNeeded();
    }
  }
  observe(subject, topic, data) {
    if (
      topic == SS_NOTIFY_CLOSED_OBJECTS_CHANGED ||
      (topic == SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH &&
        subject.ownerGlobal == getWindow())
    ) {
      this.list.updateRecentlyClosedTabs();
    }
  }
  onLoad() {
    this.list.updateRecentlyClosedTabs();
    this.addObserversIfNeeded();
  }
  handleEvent(event) {
    if (event.type == "toggle") {
      onToggleContainer(this);
    } else if (event.type == "TabSelect") {
      this.handleObservers(event.target.linkedBrowser.contentDocument);
    }
  }
  toggleContainerStyleForEmptyMsg(visible) {
    this.collapsibleContainer.classList.toggle("empty-container", visible);
  }
  getClosedTabCount = () => {
    try {
      return lazy.SessionStore.getClosedTabCount(getWindow());
    } catch (ex) {
      return 0;
    }
  };
}
customElements.define(
  "recently-closed-tabs-container",
  RecentlyClosedTabsContainer,
  {
    extends: "details",
  }
);