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

"use strict";

let gAccount, gFolders, gMessage, gExpectedAttachments;
let gDefaultTabmail, gDefaultAbout3Pane;

const URL_BASE =
  "http://mochi.test:8888/browser/comm/mail/components/extensions/test/browser/data";

/**
 * Check the parameters of a browser.onShown event was fired.
 *
 * @see mail/components/extensions/schemas/menus.json
 *
 * @param {ExtensionWrapper} extension
 * @param {object} expectedInfo
 * @param {Array} expectedInfo.menuIds
 * @param {Array} expectedInfo.contexts
 * @param {?Array} expectedInfo.attachments
 * @param {object} expectedTab
 * @param {boolean} expectedTab.active
 * @param {integer} expectedTab.index
 * @param {boolean} expectedTab.mailTab
 */
async function checkShownEvent(extension, expectedInfo, expectedTab) {
  const [info, tab] = await extension.awaitMessage("onShown");
  Assert.deepEqual(info.menuIds, expectedInfo.menuIds);
  Assert.deepEqual(info.contexts, expectedInfo.contexts);

  Assert.equal(
    !!info.attachments,
    !!expectedInfo.attachments,
    "attachments in info"
  );
  if (expectedInfo.attachments) {
    for (let i = 0; i < expectedInfo.attachments.length; i++) {
      Assert.equal(info.attachments[i].name, expectedInfo.attachments[i].name);
      Assert.equal(info.attachments[i].size, expectedInfo.attachments[i].size);
      Assert.equal(
        info.attachments[i].partName,
        expectedInfo.attachments[i].partName
      );
      Assert.equal(
        info.attachments[i].contentType,
        expectedInfo.attachments[i].contentType
      );
    }
  }

  Assert.equal(tab.active, expectedTab.active, "tab is active");
  Assert.equal(tab.index, expectedTab.index, "tab index");
  Assert.equal(tab.mailTab, expectedTab.mailTab, "tab is mailTab");
}

/**
 * Check the parameters of a browser.onClicked event was fired.
 *
 * @see mail/components/extensions/schemas/menus.json
 *
 * @param {ExtensionWrapper} extension
 * @param {object} expectedInfo
 * @param {?string} expectedInfo.menuItemId
 * @param {?Array} expectedInfo.attachments
 * @param {object} expectedTab
 * @param {boolean} expectedTab.active
 * @param {integer} expectedTab.index
 * @param {boolean} expectedTab.mailTab
 */
async function checkClickedEvent(extension, expectedInfo, expectedTab) {
  const [info, tab] = await extension.awaitMessage("onClicked");

  Assert.equal(
    !!info.attachments,
    !!expectedInfo.attachments,
    "attachments in info"
  );
  if (expectedInfo.attachments) {
    for (let i = 0; i < expectedInfo.attachments.length; i++) {
      Assert.equal(info.attachments[i].name, expectedInfo.attachments[i].name);
      Assert.equal(info.attachments[i].size, expectedInfo.attachments[i].size);
      Assert.equal(
        info.attachments[i].partName,
        expectedInfo.attachments[i].partName
      );
      Assert.equal(
        info.attachments[i].contentType,
        expectedInfo.attachments[i].contentType
      );
    }
  }

  if (expectedInfo.menuItemId) {
    Assert.equal(info.menuItemId, expectedInfo.menuItemId, "menuItemId");
  }

  Assert.equal(tab.active, expectedTab.active, "tab is active");
  Assert.equal(tab.index, expectedTab.index, "tab index");
  Assert.equal(tab.mailTab, expectedTab.mailTab, "tab is mailTab");
}

function getExtensionDetails(...permissions) {
  return {
    files: {
      "background.js": async () => {
        for (const context of [
          "message_attachments",
          "all_message_attachments",
        ]) {
          await new Promise(resolve => {
            browser.menus.create(
              {
                id: context,
                title: context,
                contexts: [context],
              },
              resolve
            );
          });
        }

        browser.menus.onShown.addListener((...args) => {
          browser.test.sendMessage("onShown", args);
        });

        browser.menus.onClicked.addListener((...args) => {
          browser.test.sendMessage("onClicked", args);
        });
        browser.test.sendMessage("menus-created");
      },
    },
    manifest: {
      applications: {
        gecko: {
          id: "menus@mochi.test",
        },
      },
      background: { scripts: ["background.js"] },
      permissions: [...permissions, "menus"],
    },
    useAddonManager: "temporary",
  };
}

add_setup(async () => {
  await Services.search.init();

  gAccount = createAccount();
  addIdentity(gAccount);
  gFolders = gAccount.incomingServer.rootFolder.subFolders;
  await createMessages(gFolders[0], {
    count: 1,
    body: {
      contentType: "text/html",
      body: await IOUtils.readUTF8(getTestFilePath(`data/content.html`)),
    },
    attachments: [
      {
        body: "I am an text attachment.",
        filename: "test1.txt",
        contentType: "text/plain",
      },
    ],
  });
  await createMessages(gFolders[0], {
    count: 1,
    body: {
      contentType: "text/html",
      body: await IOUtils.readUTF8(getTestFilePath(`data/content.html`)),
    },
    attachments: [
      {
        body: "I am an text attachment.",
        filename: "test1.txt",
        contentType: "text/plain",
      },
      {
        body: "I am another but larger attachment. ",
        filename: "test2.txt",
        contentType: "text/plain",
      },
    ],
  });

  gDefaultTabmail = document.getElementById("tabmail");
  gDefaultAbout3Pane = gDefaultTabmail.currentAbout3Pane;

  gDefaultAbout3Pane.restoreState({
    folderPaneVisible: true,
    folderURI: gFolders[0].URI,
    messagePaneVisible: true,
  });

  gExpectedAttachments = [
    {
      name: "test1.txt",
      size: 24,
      contentType: "text/plain",
      partName: "1.2",
    },
    {
      name: "test2.txt",
      size: 36,
      contentType: "text/plain",
      partName: "1.3",
    },
  ];
});

// Test a click on an attachment item.
async function subtest_attachmentItem(
  extension,
  win,
  element,
  expectedContext,
  expectedAttachments
) {
  const menu = element.ownerGlobal.document.getElementById(
    expectedContext == "message_attachments"
      ? "attachmentItemContext"
      : "attachmentListContext"
  );

  const expectedShowData = {
    menuIds: [expectedContext],
    contexts: [expectedContext, "all"],
    attachments: expectedAttachments,
  };
  const expectedClickData = {
    attachments: expectedAttachments,
  };
  const expectedTab = { active: true, index: 0, mailTab: false };

  const showEventPromise = checkShownEvent(
    extension,
    expectedShowData,
    expectedTab
  );
  await openMenuPopup(menu, element, { type: "contextmenu" });
  const menuItem = menu.querySelector(
    `#menus_mochi_test-menuitem-_${expectedContext}`
  );
  await showEventPromise;
  Assert.ok(menuItem);

  const clickEventPromise = checkClickedEvent(
    extension,
    expectedClickData,
    expectedTab
  );
  await clickItemInMenuPopup(menuItem);
  await clickEventPromise;
}

async function subtest_attachments(
  extension,
  win,
  expectedContext,
  expectedAttachments
) {
  // Test clicking on the attachmentInfo element.
  const attachmentInfo = win.document.getElementById("attachmentInfo");
  await subtest_attachmentItem(
    extension,
    win,
    attachmentInfo,
    expectedContext,
    expectedAttachments
  );

  if (expectedAttachments) {
    win.toggleAttachmentList(true);
    const attachmentList = win.document.getElementById("attachmentList");
    Assert.equal(
      attachmentList.children.length,
      expectedAttachments.length,
      "Should see the expected number of attachments."
    );

    // Test clicking on the individual attachment elements.
    for (let i = 0; i < attachmentList.children.length; i++) {
      // Select the attachment.
      attachmentList.selectItem(attachmentList.children[i]);

      // Run context click check.
      await subtest_attachmentItem(
        extension,
        win,
        attachmentList.children[i],
        "message_attachments",
        [expectedAttachments[i]]
      );
    }
  }
}

async function subtest_message_panes(
  permissions,
  expectedContext,
  expectedAttachments = null
) {
  const extensionDetails = getExtensionDetails(...permissions);

  info("Test the message pane in the 3-pane tab.");

  let extension = ExtensionTestUtils.loadExtension(extensionDetails);
  await extension.startup();
  await extension.awaitMessage("menus-created");
  await subtest_attachments(
    extension,
    gDefaultTabmail.currentAboutMessage,
    expectedContext,
    expectedAttachments
  );
  await extension.unload();

  info("Test the message pane in a tab.");

  await openMessageInTab(gMessage);
  extension = ExtensionTestUtils.loadExtension(extensionDetails);
  await extension.startup();
  await extension.awaitMessage("menus-created");
  await subtest_attachments(
    extension,
    gDefaultTabmail.currentAboutMessage,
    expectedContext,
    expectedAttachments
  );
  await extension.unload();
  gDefaultTabmail.closeOtherTabs(0);

  info("Test the message pane in a separate window.");

  const displayWindow = await openMessageInWindow(gMessage);
  extension = ExtensionTestUtils.loadExtension(extensionDetails);
  await extension.startup();
  await extension.awaitMessage("menus-created");
  await subtest_attachments(
    extension,
    displayWindow.messageBrowser.contentWindow,
    expectedContext,
    expectedAttachments
  );
  await extension.unload();
  await BrowserTestUtils.closeWindow(displayWindow);
}

// Tests using a message with one attachment.
add_task(async function test_message_panes() {
  gMessage = [...gFolders[0].messages][0];
  gDefaultAbout3Pane.threadTree.selectedIndex = 1;
  const messagePane =
    gDefaultAbout3Pane.messageBrowser.contentWindow.getMessagePaneBrowser();
  await promiseMessageLoaded(messagePane, gMessage);

  await subtest_message_panes(
    ["accountsRead", "messagesRead"],
    "message_attachments",
    [gExpectedAttachments[0]]
  );
});
add_task(async function test_message_panes_no_accounts_permission() {
  return subtest_message_panes(["messagesRead"], "message_attachments", [
    gExpectedAttachments[0],
  ]);
});
add_task(async function test_message_panes_no_messages_permission() {
  return subtest_message_panes(["accountsRead"], "message_attachments");
});
add_task(async function test_message_panes_no_permissions() {
  return subtest_message_panes([], "message_attachments");
});
