/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

import { BrowserTestUtils } from "resource://testing-common/BrowserTestUtils.sys.mjs";
import { Assert } from "resource://testing-common/Assert.sys.mjs";
import { TestUtils } from "resource://testing-common/TestUtils.sys.mjs";

function assertFirefoxViewTab(win) {
  Assert.ok(win.FirefoxViewHandler.tab, "Firefox View tab exists");
  Assert.ok(win.FirefoxViewHandler.tab?.hidden, "Firefox View tab is hidden");
  Assert.equal(
    win.gBrowser.visibleTabs.indexOf(win.FirefoxViewHandler.tab),
    -1,
    "Firefox View tab is not in the list of visible tabs"
  );
}

async function assertFirefoxViewTabSelected(win) {
  assertFirefoxViewTab(win);
  Assert.ok(
    win.FirefoxViewHandler.tab.selected,
    "Firefox View tab is selected"
  );
  await BrowserTestUtils.browserLoaded(
    win.FirefoxViewHandler.tab.linkedBrowser
  );
}

async function openFirefoxViewTab(win) {
  await BrowserTestUtils.synthesizeMouseAtCenter(
    "#firefox-view-button",
    { type: "mousedown" },
    win.browsingContext
  );
  assertFirefoxViewTab(win);
  Assert.ok(
    win.FirefoxViewHandler.tab.selected,
    "Firefox View tab is selected"
  );
  await BrowserTestUtils.browserLoaded(
    win.FirefoxViewHandler.tab.linkedBrowser
  );
  return win.FirefoxViewHandler.tab;
}

function closeFirefoxViewTab(win) {
  win.gBrowser.removeTab(win.FirefoxViewHandler.tab);
  Assert.ok(
    !win.FirefoxViewHandler.tab,
    "Reference to Firefox View tab got removed when closing the tab"
  );
}

/**
 * Run a task with Firefox View open.
 *
 * @param {Object} options
 *   Options object.
 * @param {boolean} [options.openNewWindow]
 *   Whether to run the task in a new window. If false, the current window will
 *   be used.
 * @param {boolean} [options.resetFlowManager]
 *   Whether to reset the internal state of TabsSetupFlowManager before running
 *   the task.
 * @param {function(MozBrowser)} taskFn
 *   The task to run. It can be asynchronous.
 * @returns {any}
 *   The value returned by the task.
 */
async function withFirefoxView(
  { openNewWindow = false, resetFlowManager = true },
  taskFn
) {
  const win = openNewWindow
    ? await BrowserTestUtils.openNewBrowserWindow()
    : Services.wm.getMostRecentBrowserWindow();
  if (resetFlowManager) {
    const { TabsSetupFlowManager } = ChromeUtils.importESModule(
      "resource:///modules/firefox-view-tabs-setup-manager.sys.mjs"
    );
    // reset internal state so we aren't reacting to whatever state the last invocation left behind
    TabsSetupFlowManager.resetInternalState();
  }
  // Setting this pref allows the test to run as expected with a keyboard on MacOS
  await win.SpecialPowers.pushPrefEnv({
    set: [["accessibility.tabfocus", 7]],
  });
  let tab = await openFirefoxViewTab(win);
  let originalWindow = tab.ownerGlobal;
  let result = await taskFn(tab.linkedBrowser);
  let finalWindow = tab.ownerGlobal;
  if (originalWindow == finalWindow && !tab.closing && tab.linkedBrowser) {
    // taskFn may resolve within a tick after opening a new tab.
    // We shouldn't remove the newly opened tab in the same tick.
    // Wait for the next tick here.
    await TestUtils.waitForTick();
    BrowserTestUtils.removeTab(tab);
  } else {
    Services.console.logStringMessage(
      "withFirefoxView: Tab was already closed before " +
        "removeTab would have been called"
    );
  }
  await win.SpecialPowers.popPrefEnv();
  if (openNewWindow) {
    await BrowserTestUtils.closeWindow(win);
  }
  return result;
}

function isFirefoxViewTabSelectedInWindow(win) {
  return win.gBrowser.selectedBrowser.currentURI.spec == "about:firefoxview";
}

export {
  withFirefoxView,
  assertFirefoxViewTab,
  assertFirefoxViewTabSelected,
  openFirefoxViewTab,
  closeFirefoxViewTab,
  isFirefoxViewTabSelectedInWindow,
};
