const TEST_URL =
  "https://example.com/browser/browser/base/content/test/fullscreen/open_and_focus_helper.html";

function waitForFullScreenState(browser, state, actionAfterFSEvent) {
  return new Promise(resolve => {
    let eventReceived = false;

    let observe = (subject, topic, data) => {
      if (!eventReceived) {
        return;
      }
      Services.obs.removeObserver(observe, "fullscreen-painted");
      resolve();
    };
    Services.obs.addObserver(observe, "fullscreen-painted");

    browser.ownerGlobal.addEventListener(
      `MozDOMFullscreen:${state ? "Entered" : "Exited"}`,
      () => {
        eventReceived = true;
        if (actionAfterFSEvent) {
          actionAfterFSEvent();
        }
      },
      { once: true }
    );
  });
}

/**
 * Spawns content task in browser to enter / leave fullscreen
 * @param browser - Browser to use for JS fullscreen requests
 * @param {Boolean} fullscreenState - true to enter fullscreen, false to leave
 * @returns {Promise} - Resolves once fullscreen change is applied
 */
async function changeFullscreen(browser, fullScreenState) {
  await new Promise(resolve =>
    SimpleTest.waitForFocus(resolve, browser.ownerGlobal)
  );
  let fullScreenChange = waitForFullScreenState(browser, fullScreenState);
  SpecialPowers.spawn(browser, [fullScreenState], async state => {
    // Wait for document focus before requesting full-screen
    await ContentTaskUtils.waitForCondition(
      () => content.browsingContext.isActive && content.document.hasFocus(),
      "Waiting for document focus"
    );
    if (state) {
      content.document.body.requestFullscreen();
    } else {
      content.document.exitFullscreen();
    }
  });
  return fullScreenChange;
}

async function testExpectFullScreenExit(
  browser,
  leaveFS,
  action,
  actionAfterFSEvent
) {
  let fsPromise = waitForFullScreenState(browser, false, actionAfterFSEvent);
  if (leaveFS) {
    if (action) {
      await action();
    }
    await fsPromise;
    ok(true, "Should leave full-screen");
  } else {
    if (action) {
      await action();
    }
    let result = await Promise.race([
      fsPromise,
      new Promise(resolve => {
        SimpleTest.requestFlakyTimeout("Wait for failure condition");
        // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
        setTimeout(() => resolve(true), 2500);
      }),
    ]);
    ok(result, "Should not leave full-screen");
  }
}

function jsWindowFocus(browser, iframeId) {
  return ContentTask.spawn(browser, { iframeId }, async args => {
    let destWin = content;
    if (args.iframeId) {
      let iframe = content.document.getElementById(args.iframeId);
      if (!iframe) {
        throw new Error("iframe not set");
      }
      destWin = iframe.contentWindow;
    }
    await content.wrappedJSObject.sendMessage(destWin, "focus");
  });
}

function jsElementFocus(browser, iframeId) {
  return ContentTask.spawn(browser, { iframeId }, async args => {
    let destWin = content;
    if (args.iframeId) {
      let iframe = content.document.getElementById(args.iframeId);
      if (!iframe) {
        throw new Error("iframe not set");
      }
      destWin = iframe.contentWindow;
    }
    await content.wrappedJSObject.sendMessage(destWin, "elementfocus");
  });
}

async function jsWindowOpen(browser, isPopup, iframeId) {
  //let windowOpened = BrowserTestUtils.waitForNewWindow();
  let windowOpened = isPopup
    ? BrowserTestUtils.waitForNewWindow({ url: TEST_URL })
    : BrowserTestUtils.waitForNewTab(gBrowser, TEST_URL, true);
  ContentTask.spawn(browser, { isPopup, iframeId }, async args => {
    let destWin = content;
    if (args.iframeId) {
      // Create a cross origin iframe
      destWin = (
        await content.wrappedJSObject.createIframe(args.iframeId, true)
      ).contentWindow;
    }
    // Send message to either the iframe or the current page to open a popup
    await content.wrappedJSObject.sendMessage(
      destWin,
      args.isPopup ? "openpopup" : "open"
    );
  });
  return windowOpened;
}

async function jsClickLink(browser, isPopup, iframeId) {
  //let windowOpened = BrowserTestUtils.waitForNewWindow();
  let windowOpened = isPopup
    ? BrowserTestUtils.waitForNewWindow({ url: TEST_URL })
    : BrowserTestUtils.waitForNewTab(gBrowser, TEST_URL, true);
  ContentTask.spawn(browser, { isPopup, iframeId }, async args => {
    let destWin = content;
    if (args.iframeId) {
      // Create a cross origin iframe
      destWin = (
        await content.wrappedJSObject.createIframe(args.iframeId, true)
      ).contentWindow;
    }
    // Send message to either the iframe or the current page to click a link
    await content.wrappedJSObject.sendMessage(destWin, "clicklink");
  });
  return windowOpened;
}

function waitForFocus(...args) {
  return new Promise(resolve => SimpleTest.waitForFocus(resolve, ...args));
}

function waitForBrowserWindowActive(win) {
  return new Promise(resolve => {
    if (Services.focus.activeWindow == win) {
      resolve();
    } else {
      win.addEventListener(
        "activate",
        () => {
          resolve();
        },
        { once: true }
      );
    }
  });
}
