/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const { ExperimentAPI } = ChromeUtils.importESModule(
  "resource://nimbus/ExperimentAPI.sys.mjs"
);
const { NimbusTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/NimbusTestUtils.sys.mjs"
);
const WebProtocolHandlerRegistrar = ChromeUtils.importESModule(
  "resource:///modules/WebProtocolHandlerRegistrar.sys.mjs"
).WebProtocolHandlerRegistrar.prototype;

add_setup(async () => {
  Services.prefs.setCharPref("browser.protocolhandler.loglevel", "debug");
  registerCleanupFunction(() => {
    Services.prefs.clearUserPref("browser.protocolhandler.loglevel");
    // remove all permissions in the permission manager, where we stored
    // a timeout after clicking the 'x' and 'not now' buttons.
    Services.perms.removeAll();
  });

  Services.prefs.clearUserPref("browser.mailto.dualPrompt");
  Services.prefs.clearUserPref("browser.mailto.dualPrompt.onLocationChange");

  await ExperimentAPI.ready();

  WebProtocolHandlerRegistrar._addWebProtocolHandler(
    protocol,
    "example.com",
    "example.com"
  );
});

const protocol = "mailto";
const test_domain = "example.com";

const selector_mailto_prompt =
  'notification-message[message-bar-type="infobar"]' +
  '[value="OS Protocol Registration: mailto"]';

add_task(async function check_installHash() {
  Assert.notEqual(
    null,
    WebProtocolHandlerRegistrar._getInstallHash(),
    "test to check the installHash"
  );
});

add_task(async function check_addWebProtocolHandler() {
  let currentHandler = WebProtocolHandlerRegistrar._addWebProtocolHandler(
    protocol,
    test_domain,
    "https://" + test_domain
  );

  Assert.equal(
    test_domain,
    currentHandler.name,
    "does the handler have the right name?"
  );
  Assert.equal(
    "https://" + test_domain,
    currentHandler.uriTemplate,
    "does the handler have the right uri?"
  );

  WebProtocolHandlerRegistrar._setProtocolHandlerDefault(
    protocol,
    currentHandler
  );
  WebProtocolHandlerRegistrar.removeProtocolHandler(
    protocol,
    currentHandler.uriTemplate
  );
});

/* The next test is to ensure that we offer users to configure a webmailer
 * instead of a custom executable. They will not get a prompt if they have
 * configured their OS as default handler.
 */
add_task(async function promptShownForLocalHandler() {
  let handlerApp = Cc[
    "@mozilla.org/uriloader/local-handler-app;1"
  ].createInstance(Ci.nsILocalHandlerApp);
  handlerApp.executable = Services.dirsvc.get("XREExeF", Ci.nsIFile);
  WebProtocolHandlerRegistrar._setProtocolHandlerDefault(protocol, handlerApp);

  await BrowserTestUtils.withNewTab("https://example.com/", async browser => {
    await WebProtocolHandlerRegistrar._askUserToSetMailtoHandler(
      browser,
      protocol,
      Services.io.newURI("https://example.com"),
      "https://example.com"
    );

    Assert.notEqual(
      null,
      document.querySelector(selector_mailto_prompt),
      "The prompt is shown when an executable is configured as handler."
    );
    Assert.equal(
      Glean.protocolhandlerMailto.handlerPromptShown.os_default.testGetValue(),
      1,
      "Glean records that the prompt has been displayed."
    );
  });
});

function test_rollout(
  dualPrompt = false,
  onLocationChange = false,
  dismissNotNowMinutes = 15,
  dismissXClickMinutes = 15
) {
  /* Since we use labeled counters to count how often the mailto infobar was
   * displayed or interacted with between two metrics pings, we do want to
   * make sure in testing that we start counting at 0 for each individual test
   * case which uses the rolled out feature.
   */
  Services.fog.testResetFOG();
  return NimbusTestUtils.enrollWithFeatureConfig(
    {
      featureId: NimbusFeatures.mailto.featureId,
      value: {
        dualPrompt,
        "dualPrompt.onLocationChange": onLocationChange,
        "dualPrompt.dismissXClickMinutes": dismissXClickMinutes,
        "dualPrompt.dismissNotNowMinutes": dismissNotNowMinutes,
      },
    },
    { isRollout: true }
  );
}

add_task(async function check_no_button() {
  let cleanup = await test_rollout(true, true);

  const url = "https://" + test_domain;
  WebProtocolHandlerRegistrar._addWebProtocolHandler(
    protocol,
    test_domain,
    url
  );

  await BrowserTestUtils.withNewTab("https://example.com/", async () => {
    Assert.notEqual(
      null,
      document.querySelector(selector_mailto_prompt),
      "The prompt is shown with dualPrompt.onLocationChange toggled on."
    );
  });

  await BrowserTestUtils.withNewTab("https://example.com/", async browser => {
    let button_no = document.querySelector(
      "[data-l10n-id='protocolhandler-mailto-os-handler-no-button']"
    );
    Assert.notEqual(null, button_no, "is the no-button there?");

    await button_no.click();
    Assert.equal(
      null,
      document.querySelector(selector_mailto_prompt),
      "prompt hidden after button_no clicked."
    );
    Assert.equal(
      Glean.protocolhandlerMailto.promptClicked.dismiss_os_default.testGetValue(),
      1,
      "Glean has recorded that the no button was clicked."
    );

    await WebProtocolHandlerRegistrar._askUserToSetMailtoHandler(
      browser,
      protocol,
      Services.io.newURI("https://example.com"),
      "https://example.com"
    );

    Assert.equal(
      null,
      document.querySelector(selector_mailto_prompt),
      "prompt stays hidden even when called after the no button was clicked."
    );
  });

  await cleanup();
});

add_task(async function check_x_button() {
  let timeout_x = 15;
  let cleanup = await test_rollout(true, true, 0, timeout_x);
  Services.perms.removeAll();

  await BrowserTestUtils.withNewTab("https://example.com/", async browser => {
    Assert.notEqual(
      null,
      document.querySelector(selector_mailto_prompt),
      "prompt gets shown again- the timeout for the no_button was set to zero"
    );

    let button_x = document
      .querySelector(".infobar")
      .shadowRoot.querySelector(".close");
    Assert.notEqual(null, button_x, "is the x-button there?");

    await button_x.click();
    Assert.equal(
      null,
      document.querySelector(selector_mailto_prompt),
      "prompt hidden after 'X' button clicked."
    );

    await WebProtocolHandlerRegistrar._askUserToSetMailtoHandler(
      browser,
      protocol,
      Services.io.newURI("https://example.com"),
      "https://example.com"
    );

    Assert.equal(
      null,
      document.querySelector(selector_mailto_prompt),
      "prompt stays hidden even when called after the no button was clicked."
    );

    // we expect that this test does not take more than a minute
    let expireTime = Services.perms.getPermissionObject(
      browser.contentPrincipal,
      "mailto-infobar-dismissed",
      true
    ).expireTime;

    Assert.equal(
      timeout_x * 60,
      ((expireTime - Date.now()) / 1000).toFixed(),
      "test completed within one minute, confirmed by the time after" +
        " which the permission manager would show the bar again after a dismiss."
    );

    Assert.equal(
      Glean.protocolhandlerMailto.promptClicked.dismiss_os_default.testGetValue(),
      1,
      "Glean recorded metrics about the click on the x button."
    );
  });

  await cleanup();
});

add_task(async function check_x_button() {
  let cleanup = await test_rollout(true, true, 0, 0);
  Services.perms.removeByType("mailto-infobar-dismissed");

  await BrowserTestUtils.withNewTab("https://example.com/", async () => {
    Assert.notEqual(
      null,
      document.querySelector(selector_mailto_prompt),
      "infobar shown after reset timeout for 'mailto-infobar-dismissed'" +
        " in permission manager."
    );
  });

  await cleanup();
});

add_task(async function check_bar_is_not_shown() {
  let cleanup = await test_rollout(true, false);

  await BrowserTestUtils.withNewTab("https://example.com/", async () => {
    Assert.equal(
      null,
      document.querySelector(selector_mailto_prompt),
      "Prompt is not shown, because the dualPrompt.onLocationChange is off"
    );
  });

  await cleanup();
});
