/* 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/. */

/* eslint-env webextensions */

var { MailServices } = ChromeUtils.import(
  "resource:///modules/MailServices.jsm"
);
var { MessageGenerator } = ChromeUtils.import(
  "resource://testing-common/mailnews/MessageGenerator.jsm"
);

const TEST_DOCUMENT_URL =
  "http://mochi.test:8888/browser/comm/mail/base/test/browser/files/sampleContent.html";
const TEST_MESSAGE_URL =
  "http://mochi.test:8888/browser/comm/mail/base/test/browser/files/sampleContent.eml";
const TEST_IMAGE_URL =
  "http://mochi.test:8888/browser/comm/mail/base/test/browser/files/tb-logo.png";

let about3Pane, testFolder;

async function getImageArrayBuffer() {
  let response = await fetch(TEST_IMAGE_URL);
  let blob = await response.blob();

  return new Promise((resolve, reject) => {
    let reader = new FileReader();
    reader.addEventListener("loadend", event => {
      resolve(event.target.result);
    });
    reader.readAsArrayBuffer(blob);
  });
}

function checkMenuitems(menu, ...expectedItems) {
  if (expectedItems.length == 0) {
    // Menu should not be shown.
    Assert.equal(menu.state, "closed");
    return;
  }

  Assert.notEqual(menu.state, "closed");

  let actualItems = [];
  for (let item of menu.children) {
    if (
      ["menu", "menuitem", "menugroup"].includes(item.localName) &&
      !item.hidden
    ) {
      actualItems.push(item.id);
    }
  }
  Assert.deepEqual(actualItems, expectedItems);
}

async function checkABrowser(browser, doc = browser.ownerDocument) {
  if (
    browser.webProgress?.isLoadingDocument ||
    !browser.currentURI ||
    browser.currentURI?.spec == "about:blank"
  ) {
    await BrowserTestUtils.browserLoaded(
      browser,
      undefined,
      url => url != "about:blank"
    );
  }

  let browserContext = doc.getElementById("browserContext");
  let isMac = AppConstants.platform == "macosx";
  let isWebPage =
    browser.currentURI.schemeIs("http") || browser.currentURI.schemeIs("https");
  let isExtensionPage = browser.currentURI.schemeIs("moz-extension");

  // Just some text.

  let shownPromise = BrowserTestUtils.waitForEvent(
    browserContext,
    "popupshown"
  );
  await BrowserTestUtils.synthesizeMouseAtCenter(
    "p",
    { type: "contextmenu" },
    browser
  );
  await shownPromise;

  let expectedContextItems = [];
  if (isWebPage || isExtensionPage) {
    if (isMac) {
      // Mac has the nav items directly in the context menu and not in the horizontal
      // context-navigation menugroup.
      expectedContextItems.push(
        "browserContext-back",
        "browserContext-forward",
        "browserContext-reload"
      );
    } else {
      expectedContextItems.push("context-navigation");
      checkMenuitems(
        doc.getElementById("context-navigation"),
        "browserContext-back",
        "browserContext-forward",
        "browserContext-reload"
      );
    }
  }
  if (isWebPage) {
    expectedContextItems.push("browserContext-openInBrowser");
  }
  expectedContextItems.push("browserContext-selectall");
  checkMenuitems(browserContext, ...expectedContextItems);
  browserContext.hidePopup();

  // A link.

  shownPromise = BrowserTestUtils.waitForEvent(browserContext, "popupshown");
  await BrowserTestUtils.synthesizeMouseAtCenter(
    "a",
    { type: "contextmenu" },
    browser
  );
  await shownPromise;
  checkMenuitems(
    browserContext,
    "browserContext-openLinkInBrowser",
    "browserContext-selectall",
    "browserContext-copylink",
    "browserContext-savelink"
  );
  browserContext.hidePopup();

  // A text input widget.

  await BrowserTestUtils.synthesizeMouseAtCenter("input", {}, browser);
  shownPromise = BrowserTestUtils.waitForEvent(browserContext, "popupshown");
  await BrowserTestUtils.synthesizeMouseAtCenter(
    "input",
    { type: "contextmenu" },
    browser
  );
  await shownPromise;
  checkMenuitems(
    browserContext,
    "browserContext-undo",
    "browserContext-cut",
    "browserContext-copy",
    "browserContext-paste",
    "browserContext-selectall",
    "browserContext-spell-check-enabled"
  );
  browserContext.hidePopup();

  // An image. Also checks Save Image As works.

  shownPromise = BrowserTestUtils.waitForEvent(browserContext, "popupshown");
  await BrowserTestUtils.synthesizeMouseAtCenter(
    "img",
    { type: "contextmenu" },
    browser
  );
  await shownPromise;
  checkMenuitems(
    browserContext,
    "browserContext-selectall",
    "browserContext-copyimage",
    "browserContext-saveimage"
  );

  let pickerPromise = new Promise(resolve => {
    SpecialPowers.MockFilePicker.init(window);
    SpecialPowers.MockFilePicker.showCallback = picker => {
      resolve(picker.defaultString);
      return Ci.nsIFilePicker.returnCancel;
    };
  });
  browserContext.activateItem(doc.getElementById("browserContext-saveimage"));
  Assert.equal(await pickerPromise, "tb-logo.png");
  SpecialPowers.MockFilePicker.cleanup();
}

add_setup(async function () {
  MailServices.accounts.createLocalMailAccount();
  let account = MailServices.accounts.accounts[0];
  account.addIdentity(MailServices.accounts.createIdentity());
  let rootFolder = account.incomingServer.rootFolder;
  rootFolder.createSubfolder("browserContextFolder", null);
  testFolder = rootFolder
    .getChildNamed("browserContextFolder")
    .QueryInterface(Ci.nsIMsgLocalMailFolder);
  let message = await fetch(TEST_MESSAGE_URL).then(r => r.text());
  testFolder.addMessageBatch([message]);
  let messages = new MessageGenerator().makeMessages({ count: 5 });
  let messageStrings = messages.map(message => message.toMboxString());
  testFolder.addMessageBatch(messageStrings);

  about3Pane = document.getElementById("tabmail").currentAbout3Pane;
  about3Pane.restoreState({
    folderURI: testFolder.URI,
    messagePaneVisible: true,
  });

  registerCleanupFunction(() => {
    MailServices.accounts.removeAccount(account, false);
  });
});

add_task(async function testMessagePane() {
  about3Pane.messagePane.displayWebPage(TEST_DOCUMENT_URL);
  await checkABrowser(about3Pane.webBrowser, document);
  about3Pane.messagePane.clearWebPage();
});

add_task(async function testContentTab() {
  let tab = window.openContentTab(TEST_DOCUMENT_URL);
  await checkABrowser(tab.browser);

  let tabmail = document.getElementById("tabmail");
  tabmail.closeTab(tab);
});

add_task(async function testExtensionTab() {
  let extension = ExtensionTestUtils.loadExtension({
    background: async () => {
      await browser.tabs.create({ url: "sampleContent.html" });
      browser.test.notifyPass("ready");
    },
    files: {
      "sampleContent.html": await fetch(TEST_DOCUMENT_URL).then(response =>
        response.text()
      ),
      "tb-logo.png": await getImageArrayBuffer(),
    },
  });

  await extension.startup();
  await extension.awaitFinish("ready");

  let tabmail = document.getElementById("tabmail");
  await checkABrowser(tabmail.tabInfo[1].browser);
  tabmail.closeOtherTabs(tabmail.tabInfo[0]);

  await extension.unload();
});

add_task(async function testExtensionPopupWindow() {
  let extension = ExtensionTestUtils.loadExtension({
    background: async () => {
      await browser.windows.create({
        url: "sampleContent.html",
        type: "popup",
        width: 800,
        height: 500,
      });
      browser.test.notifyPass("ready");
    },
    files: {
      "sampleContent.html": await fetch(TEST_DOCUMENT_URL).then(response =>
        response.text()
      ),
      "tb-logo.png": await getImageArrayBuffer(),
    },
  });

  await extension.startup();
  await extension.awaitFinish("ready");

  let extensionPopup = Services.wm.getMostRecentWindow("mail:extensionPopup");
  // extensionPopup.xhtml needs time to initialise properly.
  await new Promise(resolve => extensionPopup.setTimeout(resolve, 500));
  await checkABrowser(extensionPopup.document.getElementById("requestFrame"));
  await BrowserTestUtils.closeWindow(extensionPopup);

  await extension.unload();
});

add_task(async function testExtensionBrowserAction() {
  let extension = ExtensionTestUtils.loadExtension({
    files: {
      "sampleContent.html": await fetch(TEST_DOCUMENT_URL).then(response =>
        response.text()
      ),
      "tb-logo.png": await getImageArrayBuffer(),
    },
    manifest: {
      applications: {
        gecko: {
          id: "browsercontext@mochi.test",
        },
      },
      browser_action: {
        default_popup: "sampleContent.html",
      },
    },
  });

  await extension.startup();

  let { panel, browser } = await openExtensionPopup(
    window,
    "ext-browsercontext@mochi.test"
  );
  await TestUtils.waitForCondition(
    () => browser.clientWidth > 100,
    "waiting for browser to resize"
  );
  await checkABrowser(browser);
  panel.hidePopup();

  await extension.unload();
});

add_task(async function testExtensionComposeAction() {
  let extension = ExtensionTestUtils.loadExtension({
    files: {
      "sampleContent.html": await fetch(TEST_DOCUMENT_URL).then(response =>
        response.text()
      ),
      "tb-logo.png": await getImageArrayBuffer(),
    },
    manifest: {
      applications: {
        gecko: {
          id: "browsercontext@mochi.test",
        },
      },
      compose_action: {
        default_popup: "sampleContent.html",
      },
    },
  });

  await extension.startup();

  let params = Cc[
    "@mozilla.org/messengercompose/composeparams;1"
  ].createInstance(Ci.nsIMsgComposeParams);
  params.composeFields = Cc[
    "@mozilla.org/messengercompose/composefields;1"
  ].createInstance(Ci.nsIMsgCompFields);

  let composeWindowPromise = BrowserTestUtils.domWindowOpened();
  MailServices.compose.OpenComposeWindowWithParams(null, params);
  let composeWindow = await composeWindowPromise;
  await BrowserTestUtils.waitForEvent(composeWindow, "load");

  let { panel, browser } = await openExtensionPopup(
    composeWindow,
    "browsercontext_mochi_test-composeAction-toolbarbutton"
  );
  await checkABrowser(browser);
  panel.hidePopup();

  await extension.unload();
  await BrowserTestUtils.closeWindow(composeWindow);
});

add_task(async function testExtensionMessageDisplayAction() {
  let extension = ExtensionTestUtils.loadExtension({
    files: {
      "sampleContent.html": await fetch(TEST_DOCUMENT_URL).then(response =>
        response.text()
      ),
      "tb-logo.png": await getImageArrayBuffer(),
    },
    manifest: {
      applications: {
        gecko: {
          id: "browsercontext@mochi.test",
        },
      },
      message_display_action: {
        default_popup: "sampleContent.html",
      },
    },
  });

  await extension.startup();

  let messageWindowPromise = BrowserTestUtils.domWindowOpened();
  window.MsgOpenNewWindowForMessage([...testFolder.messages][0]);
  let messageWindow = await messageWindowPromise;
  let { target: aboutMessage } = await BrowserTestUtils.waitForEvent(
    messageWindow,
    "aboutMessageLoaded"
  );

  let { panel, browser } = await openExtensionPopup(
    aboutMessage,
    "browsercontext_mochi_test-messageDisplayAction-toolbarbutton"
  );
  await checkABrowser(browser);
  panel.hidePopup();

  await extension.unload();
  await BrowserTestUtils.closeWindow(messageWindow);
});
