"use strict";

const { AppMenuNotifications } = ChromeUtils.importESModule(
  "resource://gre/modules/AppMenuNotifications.sys.mjs"
);

/**
 * Tests that when we click on the main call-to-action of the doorhanger, the provided
 * action is called, and the doorhanger removed.
 */
add_task(async function testMainActionCalled() {
  let options = {
    gBrowser: window.gBrowser,
    url: "about:blank",
  };

  await BrowserTestUtils.withNewTab(options, function (browser) {
    is(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is closed."
    );
    let mainActionCalled = false;
    let mainAction = {
      callback: () => {
        mainActionCalled = true;
      },
    };
    AppMenuNotifications.showNotification("update-manual", mainAction);

    isnot(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is showing."
    );
    let notifications = [...PanelUI.notificationPanel.children].filter(
      n => !n.hidden
    );
    is(
      notifications.length,
      1,
      "PanelUI doorhanger is only displaying one notification."
    );
    let doorhanger = notifications[0];
    is(
      doorhanger.id,
      "appMenu-update-manual-notification",
      "PanelUI is displaying the update-manual notification."
    );

    let button = doorhanger.button;
    button.click();

    ok(mainActionCalled, "Main action callback was called");
    is(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is closed."
    );
    is(
      PanelUI.menuButton.hasAttribute("badge-status"),
      false,
      "Should not have a badge status"
    );
  });
});

/**
 * This tests that when we click the secondary action for a notification,
 * it will display the badge for that notification on the PanelUI menu button.
 * Once we click on this button, we should see an item in the menu which will
 * call our main action.
 */
add_task(async function testSecondaryActionWorkflow() {
  let options = {
    gBrowser: window.gBrowser,
    url: "about:blank",
  };

  await BrowserTestUtils.withNewTab(options, async function (browser) {
    is(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is closed."
    );

    let mainActionCalled = false;
    let mainAction = {
      callback: () => {
        mainActionCalled = true;
      },
    };
    AppMenuNotifications.showNotification("update-manual", mainAction);

    isnot(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is showing."
    );
    let notifications = [...PanelUI.notificationPanel.children].filter(
      n => !n.hidden
    );
    is(
      notifications.length,
      1,
      "PanelUI doorhanger is only displaying one notification."
    );
    let doorhanger = notifications[0];
    is(
      doorhanger.id,
      "appMenu-update-manual-notification",
      "PanelUI is displaying the update-manual notification."
    );

    let secondaryActionButton = doorhanger.secondaryButton;
    secondaryActionButton.click();

    is(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is closed."
    );

    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-manual",
      "Badge is displaying on PanelUI button."
    );

    await gCUITestUtils.openMainMenu();
    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-manual",
      "Badge is displaying on PanelUI button."
    );
    let menuItem = PanelUI.mainView.querySelector(".panel-banner-item");
    is(
      menuItem.getAttribute("data-l10n-id"),
      "appmenuitem-banner-update-manual",
      "Showing correct label"
    );
    is(menuItem.hidden, false, "update-manual menu item is showing.");

    await gCUITestUtils.hideMainMenu();
    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-manual",
      "Badge is shown on PanelUI button."
    );

    await gCUITestUtils.openMainMenu();
    menuItem.click();
    ok(mainActionCalled, "Main action callback was called");

    AppMenuNotifications.removeNotification(/.*/);
  });
});

/**
 * This tests that the PanelUI update downloading badge and banner
 * notification are correctly displayed and that clicking the banner
 * item calls the main action.
 */
add_task(async function testDownloadingBadge() {
  let options = {
    gBrowser: window.gBrowser,
    url: "about:blank",
  };

  await BrowserTestUtils.withNewTab(options, async function (browser) {
    let mainActionCalled = false;
    let mainAction = {
      callback: () => {
        mainActionCalled = true;
      },
    };
    // The downloading notification is always displayed in a dismissed state.
    AppMenuNotifications.showNotification(
      "update-downloading",
      mainAction,
      undefined,
      { dismissed: true }
    );
    is(PanelUI.notificationPanel.state, "closed", "doorhanger is closed.");

    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-downloading",
      "Downloading badge is displaying on PanelUI button."
    );

    await gCUITestUtils.openMainMenu();
    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-downloading",
      "Downloading badge is displaying on PanelUI button."
    );
    let menuItem = PanelUI.mainView.querySelector(".panel-banner-item");
    is(
      menuItem.getAttribute("data-l10n-id"),
      "appmenuitem-banner-update-downloading",
      "Showing correct label (downloading)"
    );
    is(menuItem.hidden, false, "update-downloading menu item is showing.");

    await gCUITestUtils.hideMainMenu();
    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-downloading",
      "Downloading badge is shown on PanelUI button."
    );

    await gCUITestUtils.openMainMenu();
    menuItem.click();
    ok(mainActionCalled, "Main action callback was called");

    AppMenuNotifications.removeNotification(/.*/);
  });
});

/**
 * We want to ensure a few things with this:
 * - Adding a doorhanger will make a badge disappear
 * - once the notification for the doorhanger is resolved (removed, not just dismissed),
 *   then we display any other badges that are remaining.
 */
add_task(async function testInteractionWithBadges() {
  await BrowserTestUtils.withNewTab("about:blank", async function (browser) {
    // Remove the fxa toolbar button from the navbar to ensure the notification
    // is displayed on the app menu button.
    let { CustomizableUI } = ChromeUtils.importESModule(
      "resource:///modules/CustomizableUI.sys.mjs"
    );
    CustomizableUI.removeWidgetFromArea("fxa-toolbar-menu-button");

    AppMenuNotifications.showBadgeOnlyNotification("fxa-needs-authentication");
    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "fxa-needs-authentication",
      "Fxa badge is shown on PanelUI button."
    );
    is(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is closed."
    );

    let mainActionCalled = false;
    let mainAction = {
      callback: () => {
        mainActionCalled = true;
      },
    };
    AppMenuNotifications.showNotification("update-manual", mainAction);

    isnot(
      PanelUI.menuButton.getAttribute("badge-status"),
      "fxa-needs-authentication",
      "Fxa badge is hidden on PanelUI button."
    );
    isnot(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is showing."
    );
    let notifications = [...PanelUI.notificationPanel.children].filter(
      n => !n.hidden
    );
    is(
      notifications.length,
      1,
      "PanelUI doorhanger is only displaying one notification."
    );
    let doorhanger = notifications[0];
    is(
      doorhanger.id,
      "appMenu-update-manual-notification",
      "PanelUI is displaying the update-manual notification."
    );

    let secondaryActionButton = doorhanger.secondaryButton;
    secondaryActionButton.click();

    is(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is closed."
    );

    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-manual",
      "Badge is displaying on PanelUI button."
    );

    await gCUITestUtils.openMainMenu();
    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-manual",
      "Badge is displaying on PanelUI button."
    );
    let menuItem = PanelUI.mainView.querySelector(".panel-banner-item");
    is(
      menuItem.getAttribute("data-l10n-id"),
      "appmenuitem-banner-update-manual",
      "Showing correct label"
    );
    is(menuItem.hidden, false, "update-manual menu item is showing.");

    menuItem.click();
    ok(mainActionCalled, "Main action callback was called");

    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "fxa-needs-authentication",
      "Fxa badge is shown on PanelUI button."
    );
    AppMenuNotifications.removeNotification(/.*/);
    is(
      PanelUI.menuButton.hasAttribute("badge-status"),
      false,
      "Should not have a badge status"
    );
  });
});

/**
 * This tests that adding a badge will not dismiss any existing doorhangers.
 */
add_task(async function testAddingBadgeWhileDoorhangerIsShowing() {
  await BrowserTestUtils.withNewTab("about:blank", function (browser) {
    is(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is closed."
    );
    let mainActionCalled = false;
    let mainAction = {
      callback: () => {
        mainActionCalled = true;
      },
    };
    AppMenuNotifications.showNotification("update-manual", mainAction);
    AppMenuNotifications.showBadgeOnlyNotification("fxa-needs-authentication");

    isnot(
      PanelUI.menuButton.getAttribute("badge-status"),
      "fxa-needs-authentication",
      "Fxa badge is hidden on PanelUI button."
    );
    isnot(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is showing."
    );
    let notifications = [...PanelUI.notificationPanel.children].filter(
      n => !n.hidden
    );
    is(
      notifications.length,
      1,
      "PanelUI doorhanger is only displaying one notification."
    );
    let doorhanger = notifications[0];
    is(
      doorhanger.id,
      "appMenu-update-manual-notification",
      "PanelUI is displaying the update-manual notification."
    );

    let mainActionButton = doorhanger.button;
    mainActionButton.click();

    ok(mainActionCalled, "Main action callback was called");
    is(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is closed."
    );
    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "fxa-needs-authentication",
      "Fxa badge is shown on PanelUI button."
    );
    AppMenuNotifications.removeNotification(/.*/);
    is(
      PanelUI.menuButton.hasAttribute("badge-status"),
      false,
      "Should not have a badge status"
    );
  });
});

/**
 * Tests that badges operate like a stack.
 */
add_task(async function testMultipleBadges() {
  await BrowserTestUtils.withNewTab("about:blank", async function (browser) {
    let doc = browser.ownerDocument;
    let menuButton = doc.getElementById("PanelUI-menu-button");

    is(
      menuButton.hasAttribute("badge-status"),
      false,
      "Should not have a badge status"
    );
    is(
      menuButton.hasAttribute("badge"),
      false,
      "Should not have the badge attribute set"
    );

    AppMenuNotifications.showBadgeOnlyNotification("fxa-needs-authentication");
    is(
      menuButton.getAttribute("badge-status"),
      "fxa-needs-authentication",
      "Should have fxa-needs-authentication badge status"
    );

    AppMenuNotifications.showBadgeOnlyNotification("update-succeeded");
    is(
      menuButton.getAttribute("badge-status"),
      "update-succeeded",
      "Should have update-succeeded badge status (update > fxa)"
    );

    AppMenuNotifications.showBadgeOnlyNotification("update-failed");
    is(
      menuButton.getAttribute("badge-status"),
      "update-failed",
      "Should have update-failed badge status"
    );

    AppMenuNotifications.removeNotification(/^update-/);
    is(
      menuButton.getAttribute("badge-status"),
      "fxa-needs-authentication",
      "Should have fxa-needs-authentication badge status"
    );

    AppMenuNotifications.removeNotification(/^fxa-/);
    is(
      menuButton.hasAttribute("badge-status"),
      false,
      "Should not have a badge status"
    );

    await gCUITestUtils.openMainMenu();
    is(
      menuButton.hasAttribute("badge-status"),
      false,
      "Should not have a badge status (Hamburger menu opened)"
    );
    await gCUITestUtils.hideMainMenu();

    AppMenuNotifications.showBadgeOnlyNotification("fxa-needs-authentication");
    AppMenuNotifications.showBadgeOnlyNotification("update-succeeded");
    AppMenuNotifications.removeNotification(/.*/);
    is(
      menuButton.hasAttribute("badge-status"),
      false,
      "Should not have a badge status"
    );
  });
});

/**
 * Tests that non-badges also operate like a stack.
 */
add_task(async function testMultipleNonBadges() {
  await BrowserTestUtils.withNewTab("about:blank", async function (browser) {
    is(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is closed."
    );

    let updateManualAction = {
      called: false,
      callback: () => {
        updateManualAction.called = true;
      },
    };
    let updateRestartAction = {
      called: false,
      callback: () => {
        updateRestartAction.called = true;
      },
    };

    AppMenuNotifications.showNotification("update-manual", updateManualAction);

    let notifications;
    let doorhanger;

    isnot(PanelUI.notificationPanel.state, "closed", "Doorhanger is showing.");
    notifications = [...PanelUI.notificationPanel.children].filter(
      n => !n.hidden
    );
    is(
      notifications.length,
      1,
      "PanelUI doorhanger is only displaying one notification."
    );
    doorhanger = notifications[0];
    is(
      doorhanger.id,
      "appMenu-update-manual-notification",
      "PanelUI is displaying the update-manual notification."
    );

    AppMenuNotifications.showNotification(
      "update-restart",
      updateRestartAction
    );

    isnot(PanelUI.notificationPanel.state, "closed", "Doorhanger is showing.");
    notifications = [...PanelUI.notificationPanel.children].filter(
      n => !n.hidden
    );
    is(
      notifications.length,
      1,
      "PanelUI doorhanger is only displaying one notification."
    );
    doorhanger = notifications[0];
    is(
      doorhanger.id,
      "appMenu-update-restart-notification",
      "PanelUI is displaying the update-restart notification."
    );

    let secondaryActionButton = doorhanger.secondaryButton;
    secondaryActionButton.click();

    is(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is closed."
    );
    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-restart",
      "update-restart badge is displaying on PanelUI button."
    );

    await gCUITestUtils.openMainMenu();
    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-restart",
      "update-restart badge is displaying on PanelUI button."
    );
    let menuItem = PanelUI.mainView.querySelector(".panel-banner-item");
    is(
      menuItem.getAttribute("data-l10n-id"),
      "appmenuitem-banner-update-restart",
      "Showing correct label"
    );
    is(menuItem.hidden, false, "update-restart menu item is showing.");

    menuItem.click();
    ok(
      updateRestartAction.called,
      "update-restart main action callback was called"
    );

    is(
      PanelUI.notificationPanel.state,
      "closed",
      "update-manual doorhanger is closed."
    );
    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-manual",
      "update-manual badge is displaying on PanelUI button."
    );

    await gCUITestUtils.openMainMenu();
    is(
      PanelUI.menuButton.getAttribute("badge-status"),
      "update-manual",
      "update-manual badge is displaying on PanelUI button."
    );
    is(
      menuItem.getAttribute("data-l10n-id"),
      "appmenuitem-banner-update-manual",
      "Showing correct label"
    );
    is(menuItem.hidden, false, "update-manual menu item is showing.");

    menuItem.click();
    ok(
      updateManualAction.called,
      "update-manual main action callback was called"
    );
  });
});
