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

const { MessageGenerator } = ChromeUtils.importESModule(
  "resource://testing-common/mailnews/MessageGenerator.sys.mjs"
);
const { click_through_appmenu } = ChromeUtils.importESModule(
  "resource://testing-common/mail/WindowHelpers.sys.mjs"
);
const { ensure_cards_view } = ChromeUtils.importESModule(
  "resource://testing-common/MailViewHelpers.sys.mjs"
);

const tabmail = document.getElementById("tabmail");
const about3Pane = tabmail.currentAbout3Pane;
const { threadPane, threadTree } = about3Pane;
let rootFolder, testFolder, testMessages, displayContext, displayButton;

add_setup(async function () {
  const account = MailServices.accounts.createLocalMailAccount();
  account.addIdentity(MailServices.accounts.createIdentity());
  rootFolder = account.incomingServer.rootFolder.QueryInterface(
    Ci.nsIMsgLocalMailFolder
  );
  testFolder = rootFolder
    .createLocalSubfolder("cardsView")
    .QueryInterface(Ci.nsIMsgLocalMailFolder);

  const generator = new MessageGenerator();
  testFolder.addMessageBatch(
    generator
      .makeMessages({ count: 5 })
      .map(message => message.toMessageString())
  );
  testMessages = [...testFolder.messages];

  about3Pane.displayFolder(testFolder.URI);
  about3Pane.paneLayout.messagePaneVisible = false;

  registerCleanupFunction(() => {
    MailServices.accounts.removeAccount(account, false);
    about3Pane.paneLayout.messagePaneVisible = true;
    about3Pane.folderTree.focus();
  });
});

add_task(async function testSwitchToCardsView() {
  Assert.equal(
    threadTree.getAttribute("rows"),
    "thread-card",
    "The tree view should have a card layout"
  );

  await click_through_appmenu(
    [{ id: "appmenu_View" }, { id: "appmenu_MessagePaneLayout" }],
    { id: "appmenu_messagePaneClassic" },
    window
  );

  Assert.equal(
    threadTree.getAttribute("rows"),
    "thread-card",
    "tree view in cards layout"
  );

  displayContext = about3Pane.document.getElementById(
    "threadPaneDisplayContext"
  );
  displayButton = about3Pane.document.getElementById("threadPaneDisplayButton");
  let shownPromise = BrowserTestUtils.waitForEvent(
    displayContext,
    "popupshown"
  );
  EventUtils.synthesizeMouseAtCenter(displayButton, {}, about3Pane);
  await shownPromise;

  Assert.ok(
    displayContext
      .querySelector("#threadPaneCardsView")
      .getAttribute("checked"),
    "The cards view menuitem should be checked"
  );

  let hiddenPromise = BrowserTestUtils.waitForEvent(
    displayContext,
    "popuphidden"
  );
  const switchedToTable = BrowserTestUtils.waitForAttribute(
    "rows",
    threadTree,
    "thread-row"
  );
  displayContext.activateItem(
    displayContext.querySelector("#threadPaneTableView")
  );
  await switchedToTable;
  EventUtils.synthesizeKey("KEY_Escape", {});
  await hiddenPromise;

  await click_through_appmenu(
    [{ id: "appmenu_View" }, { id: "appmenu_MessagePaneLayout" }],
    { id: "appmenu_messagePaneVertical" },
    window
  );

  Assert.equal(
    threadTree.getAttribute("rows"),
    "thread-row",
    "The tree view should not switch to a card layout"
  );
  Assert.equal(
    threadTree.table.body.getAttribute("role"),
    "treegrid",
    "The message list table should be presented as Tree Grid View"
  );
  Assert.equal(
    threadTree.getRowAtIndex(0).getAttribute("role"),
    "row",
    "The message row should be presented as Row Item"
  );

  displayContext = about3Pane.document.getElementById(
    "threadPaneDisplayContext"
  );
  displayButton = about3Pane.document.getElementById("threadPaneDisplayButton");
  shownPromise = BrowserTestUtils.waitForEvent(displayContext, "popupshown");
  EventUtils.synthesizeMouseAtCenter(displayButton, {}, about3Pane);
  await shownPromise;

  Assert.ok(
    displayContext
      .querySelector("#threadPaneTableView")
      .getAttribute("checked"),
    "The table view menuitem should be checked"
  );

  hiddenPromise = BrowserTestUtils.waitForEvent(displayContext, "popuphidden");
  const switchedToCards = BrowserTestUtils.waitForAttribute(
    "rows",
    threadTree,
    "thread-card"
  );
  displayContext.activateItem(
    displayContext.querySelector("#threadPaneCardsView")
  );
  await switchedToCards;
  EventUtils.synthesizeKey("KEY_Escape", {});
  await hiddenPromise;
  await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));

  Assert.equal(
    threadTree.getAttribute("rows"),
    "thread-card",
    "tree view in cards layout"
  );
  Assert.equal(
    threadTree.table.body.getAttribute("role"),
    "treegrid",
    "The message list table should remain as Tree Grid View"
  );
  Assert.equal(
    threadTree.getRowAtIndex(0).getAttribute("role"),
    "row",
    "The message row should remain as Row Item"
  );

  const tableRow = threadTree.getRowAtIndex(1);
  const tableData = tableRow.querySelector(".card-container");
  const rowHeight = getComputedStyle(tableRow).getPropertyValue("height");
  const containerHeightNoPadding = getComputedStyle(tableData)
    .getPropertyValue("height")
    .slice(0, -2);
  // Add 4 px of padding to the computed height.
  const containerHeight = parseFloat(containerHeightNoPadding) + 4;
  Assert.equal(
    rowHeight,
    `${containerHeight}px`,
    "The message and content container height should be the same"
  );

  const row = threadTree.getRowAtIndex(0);
  const star = row.querySelector(".button-star");
  Assert.ok(BrowserTestUtils.isVisible(star), "star icon should be visible");
  const tag = row.querySelector(".tag-icon");
  Assert.ok(BrowserTestUtils.isHidden(tag), "tag icon should be hidden");
  const attachment = row.querySelector(".attachment-icon");
  Assert.ok(
    BrowserTestUtils.isHidden(attachment),
    "attachment icon should be hidden"
  );

  // Switching to horizontal view shouldn't affect the list layout.
  await click_through_appmenu(
    [{ id: "appmenu_View" }, { id: "appmenu_MessagePaneLayout" }],
    { id: "appmenu_messagePaneClassic" },
    window
  );

  Assert.equal(
    threadTree.getAttribute("rows"),
    "thread-card",
    "tree view in cards layout"
  );
  about3Pane.folderTree.focus();
});

add_task(async function testTagsInVerticalView() {
  const row = threadTree.getRowAtIndex(1);
  EventUtils.synthesizeMouseAtCenter(row, {}, about3Pane);
  Assert.ok(row.classList.contains("selected"), "the row should be selected");

  const tag = row.querySelector(".tag-icon");
  Assert.ok(BrowserTestUtils.isHidden(tag), "tag icon should be hidden");

  // Set the important tag.
  EventUtils.synthesizeKey("1", {});
  Assert.ok(BrowserTestUtils.isVisible(tag), "tag icon should be visible");
  Assert.deepEqual(tag.title, "Important", "The important tag should be set");

  const row2 = threadTree.getRowAtIndex(2);
  EventUtils.synthesizeMouseAtCenter(row2, {}, about3Pane);
  Assert.ok(
    row2.classList.contains("selected"),
    "the third row should be selected"
  );

  const tag2 = row2.querySelector(".tag-icon");
  Assert.ok(BrowserTestUtils.isHidden(tag2), "tag icon should be hidden");

  // Set the work tag.
  EventUtils.synthesizeKey("2", {});
  Assert.ok(BrowserTestUtils.isVisible(tag2), "tag icon should be visible");
  Assert.deepEqual(tag2.title, "Work", "The work tag should be set");

  // Switch back to a table layout and horizontal view.
  const shownPromise = BrowserTestUtils.waitForEvent(
    displayContext,
    "popupshown"
  );
  EventUtils.synthesizeMouseAtCenter(displayButton, {}, about3Pane);
  await shownPromise;

  Assert.ok(
    displayContext
      .querySelector("#threadPaneCardsView")
      .getAttribute("checked"),
    "The cards view menuitem should be checked"
  );

  const hiddenPromise = BrowserTestUtils.waitForEvent(
    displayContext,
    "popuphidden"
  );
  const switchedToTable = BrowserTestUtils.waitForAttribute(
    "rows",
    threadTree,
    "thread-row"
  );
  displayContext.activateItem(
    displayContext.querySelector("#threadPaneTableView")
  );
  await switchedToTable;
  EventUtils.synthesizeKey("KEY_Escape", {});
  await hiddenPromise;

  Assert.equal(
    threadTree.getAttribute("rows"),
    "thread-row",
    "tree view in table layout"
  );

  await ensure_cards_view(document);
  about3Pane.folderTree.focus();
}).skip(); // TODO: update the test for tags on Bug 1860900.

/**
 * This test Checks that:
 * Clicking on the kebab button of a row selects that row.
 * If other rows are selected or multiselection, only the row clicked should be selected.
 * The menu popup opens.
 */
add_task(async function testMessageMenuButton() {
  const row1 = threadTree.getRowAtIndex(1);
  const row2 = threadTree.getRowAtIndex(2);
  const menuContext = about3Pane.document.getElementById("mailContext");
  const menuButton = row1.querySelector(".tree-button-more");
  EventUtils.synthesizeMouseAtCenter(row1, {}, about3Pane);
  EventUtils.synthesizeMouseAtCenter(row2, { shiftKey: true }, about3Pane);
  Assert.ok(row1.classList.contains("selected"), "Row 1 should be selected");
  Assert.ok(row2.classList.contains("selected"), "Row 2 should be selected");
  EventUtils.synthesizeMouseAtCenter(menuButton, {}, about3Pane);
  await BrowserTestUtils.waitForPopupEvent(menuContext, "shown");
  Assert.ok(
    BrowserTestUtils.isVisible(menuContext),
    "The message card's menu should be visible"
  );
  Assert.ok(
    row1.classList.contains("selected"),
    "The row that was clicked should be selected"
  );
  Assert.ok(
    !row2.classList.contains("selected"),
    "The row that wasn't clicked should be unselected"
  );
  menuContext.hidePopup();
  await BrowserTestUtils.waitForPopupEvent(menuContext, "hidden");
  about3Pane.folderTree.focus();
});
