/* 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 { MozLitElement } from "chrome://global/content/lit-utils.mjs";

// eslint-disable-next-line import/no-unassigned-import
import "chrome://browser/content/firefoxview/card-container.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://browser/content/firefoxview/fxview-empty-state.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://browser/content/firefoxview/fxview-search-textbox.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://browser/content/firefoxview/fxview-tab-list.mjs";

import { placeLinkOnClipboard } from "./helpers.mjs";

const lazy = {};

ChromeUtils.defineESModuleGetters(lazy, {
  DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
});

const WIN_RESIZE_DEBOUNCE_RATE_MS = 500;
const WIN_RESIZE_DEBOUNCE_TIMEOUT_MS = 1000;

/**
 * A base class for content container views displayed on firefox-view.
 *
 * @property {boolean} recentBrowsing
 *   Is part of the recentbrowsing page view
 * @property {boolean} paused
 *   No content will be updated and rendered while paused
 */
export class ViewPageContent extends MozLitElement {
  static get properties() {
    return {
      recentBrowsing: { type: Boolean },
      paused: { type: Boolean },
    };
  }
  constructor() {
    super();
    // don't update or render until explicitly un-paused
    this.paused = true;
  }

  get ownerViewPage() {
    return this.closest("[type='page']") || this;
  }

  get isVisible() {
    if (!this.isConnected || this.ownerDocument.visibilityState != "visible") {
      return false;
    }
    return this.ownerViewPage.selectedTab;
  }

  /**
   * Override this function to run a callback whenever this content is visible.
   */
  viewVisibleCallback() {}

  /**
   * Override this function to run a callback whenever this content is hidden.
   */
  viewHiddenCallback() {}

  getWindow() {
    return window.browsingContext.embedderWindowGlobal.browsingContext.window;
  }

  get isSelectedBrowserTab() {
    const { gBrowser } = this.getWindow();
    return gBrowser.selectedBrowser.browsingContext == window.browsingContext;
  }

  copyLink(e) {
    placeLinkOnClipboard(this.triggerNode.title, this.triggerNode.url);
    this.recordContextMenuTelemetry("copy-link", e);
  }

  openInNewWindow(e) {
    this.getWindow().openTrustedLinkIn(this.triggerNode.url, "window", {
      private: false,
    });
    this.recordContextMenuTelemetry("open-in-new-window", e);
  }

  openInNewPrivateWindow(e) {
    this.getWindow().openTrustedLinkIn(this.triggerNode.url, "window", {
      private: true,
    });
    this.recordContextMenuTelemetry("open-in-private-window", e);
  }

  recordContextMenuTelemetry(menuAction, event) {
    Glean.firefoxviewNext.contextMenuTabs.record({
      menu_action: menuAction,
      data_type: event.target.panel.dataset.tabType,
    });
  }

  shouldUpdate(changedProperties) {
    return !this.paused && super.shouldUpdate(changedProperties);
  }
}

/**
 * A "page" in firefox view, which may be hidden or shown by the named-deck container or
 * via the owner document's visibility
 *
 * @property {boolean} selectedTab
 *   Is this page the selected view in the named-deck container
 */
export class ViewPage extends ViewPageContent {
  static get properties() {
    return {
      selectedTab: { type: Boolean },
      searchTextboxSize: { type: Number },
    };
  }

  constructor() {
    super();
    this.selectedTab = false;
    this.recentBrowsing = Boolean(this.recentBrowsingElement);
    this.onTabSelect = this.onTabSelect.bind(this);
    this.onResize = this.onResize.bind(this);
  }

  get recentBrowsingElement() {
    return this.closest("VIEW-RECENTBROWSING");
  }

  onResize() {
    this.windowResizeTask = new lazy.DeferredTask(
      () => this.updateAllVirtualLists(),
      WIN_RESIZE_DEBOUNCE_RATE_MS,
      WIN_RESIZE_DEBOUNCE_TIMEOUT_MS
    );
    this.windowResizeTask?.arm();
  }

  onTabSelect({ target }) {
    const win = target.ownerGlobal;

    let selfBrowser = window.docShell?.chromeEventHandler;
    const { gBrowser } = this.getWindow();
    let isForegroundTab = gBrowser.selectedBrowser == selfBrowser;

    if (win.FirefoxViewHandler.tab?.selected && isForegroundTab) {
      this.paused = false;
      this.viewVisibleCallback();
    } else {
      this.paused = true;
      this.viewHiddenCallback();
    }
  }

  connectedCallback() {
    super.connectedCallback();
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    this.getWindow().removeEventListener("resize", this.onResize);
    this.getWindow().removeEventListener("TabSelect", this.onTabSelect);
  }

  updateAllVirtualLists() {
    if (!this.paused) {
      let tabLists = [];
      if (this.recentBrowsing) {
        let viewComponents = this.querySelectorAll("[slot]");
        viewComponents.forEach(viewComponent => {
          let currentTabLists = [];
          if (viewComponent.nodeName.includes("OPENTABS")) {
            viewComponent.viewCards.forEach(viewCard => {
              currentTabLists.push(viewCard.tabList);
            });
          } else {
            currentTabLists =
              viewComponent.shadowRoot.querySelectorAll("fxview-tab-list");
          }
          tabLists.push(...currentTabLists);
        });
      } else {
        tabLists = this.shadowRoot.querySelectorAll("fxview-tab-list");
      }
      tabLists.forEach(tabList => {
        if (!tabList.updatesPaused && tabList.rootVirtualListEl?.isVisible) {
          tabList.rootVirtualListEl.recalculateAfterWindowResize();
        }
      });
    }
  }

  toggleVisibilityInCardContainer(isOpenTabs) {
    let cards = [];
    let tabLists = [];
    if (!isOpenTabs) {
      cards = this.shadowRoot.querySelectorAll("card-container");
      tabLists = this.shadowRoot.querySelectorAll(
        "fxview-tab-list, syncedtabs-tab-list"
      );
    } else {
      this.viewCards.forEach(viewCard => {
        if (viewCard.cardEl) {
          cards.push(viewCard.cardEl);
          tabLists.push(viewCard.tabList);
        }
      });
    }
    if (tabLists.length && cards.length) {
      cards.forEach(cardEl => {
        if (cardEl.visible !== !this.paused) {
          cardEl.visible = !this.paused;
        } else if (
          cardEl.isExpanded &&
          Array.from(tabLists).some(
            tabList => tabList.updatesPaused !== this.paused
          )
        ) {
          // If card is already visible and expanded but tab-list has updatesPaused,
          // update the tab-list updatesPaused prop from here instead of card-container
          tabLists.forEach(tabList => {
            tabList.updatesPaused = this.paused;
          });
        }
      });
    }
  }

  enter() {
    this.selectedTab = true;
    if (this.isVisible) {
      this.paused = false;
      this.viewVisibleCallback();
      this.getWindow().addEventListener("resize", this.onResize);
      this.getWindow().addEventListener("TabSelect", this.onTabSelect);
    }
  }

  exit() {
    this.selectedTab = false;
    this.paused = true;
    this.viewHiddenCallback();
    if (!this.windowResizeTask?.isFinalized) {
      this.windowResizeTask?.finalize();
    }
    this.getWindow().removeEventListener("resize", this.onResize);
    this.getWindow().removeEventListener("TabSelect", this.onTabSelect);
  }
}
