/** @import MozSelect, {MozOption} from "../../../../toolkit/content/widgets/moz-select/moz-select.mjs" */
/** @import MozBoxItem from "../../../../toolkit/content/widgets/moz-box-item/moz-box-item.mjs";*/

SimpleTest.requestCompleteLog();
const { HandlerServiceTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/HandlerServiceTestUtils.sys.mjs"
);

let gHandlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(
  Ci.nsIHandlerService
);

let gOldMailHandlers = [];
let gDummyHandlers = [];
let gOriginalPreferredMailHandler;
let gOriginalPreferredPDFHandler;

/**
 * @type {Promise<void>}
 */
let appHandlerInitialized;

registerCleanupFunction(function () {
  function removeDummyHandlers(handlers) {
    // Remove any of the dummy handlers we created.
    for (let i = handlers.Count() - 1; i >= 0; i--) {
      try {
        if (
          gDummyHandlers.some(
            h =>
              h.uriTemplate ==
              handlers.queryElementAt(i, Ci.nsIWebHandlerApp).uriTemplate
          )
        ) {
          handlers.removeElementAt(i);
        }
      } catch (ex) {
        /* ignore non-web-app handlers */
      }
    }
  }
  // Re-add the original protocol handlers:
  let mailHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("mailto");
  let mailHandlers = mailHandlerInfo.possibleApplicationHandlers;
  for (let h of gOldMailHandlers) {
    mailHandlers.appendElement(h);
  }
  removeDummyHandlers(mailHandlers);
  mailHandlerInfo.preferredApplicationHandler = gOriginalPreferredMailHandler;
  gHandlerService.store(mailHandlerInfo);

  let pdfHandlerInfo =
    HandlerServiceTestUtils.getHandlerInfo("application/pdf");
  removeDummyHandlers(pdfHandlerInfo.possibleApplicationHandlers);
  pdfHandlerInfo.preferredApplicationHandler = gOriginalPreferredPDFHandler;
  gHandlerService.store(pdfHandlerInfo);

  gBrowser.removeCurrentTab();
});

function scrubMailtoHandlers(handlerInfo) {
  // Remove extant web handlers because they have icons that
  // we fetch from the web, which isn't allowed in tests.
  let handlers = handlerInfo.possibleApplicationHandlers;
  for (let i = handlers.Count() - 1; i >= 0; i--) {
    try {
      let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp);
      gOldMailHandlers.push(handler);
      // If we get here, this is a web handler app. Remove it:
      handlers.removeElementAt(i);
    } catch (ex) {}
  }
}

add_setup(async function () {
  await SpecialPowers.pushPrefEnv({
    set: [["browser.settings-redesign.enabled", true]],
  });

  // Create our dummy handlers
  let handler1 = Cc["@mozilla.org/uriloader/web-handler-app;1"].createInstance(
    Ci.nsIWebHandlerApp
  );
  handler1.name = "Handler 1";
  handler1.uriTemplate = "https://example.com/first/%s";

  let handler2 = Cc["@mozilla.org/uriloader/web-handler-app;1"].createInstance(
    Ci.nsIWebHandlerApp
  );
  handler2.name = "Handler 2";
  handler2.uriTemplate = "https://example.org/second/%s";
  gDummyHandlers.push(handler1, handler2);

  function substituteWebHandlers(handlerInfo) {
    // Append the dummy handlers to replace them:
    let handlers = handlerInfo.possibleApplicationHandlers;
    handlers.appendElement(handler1);
    handlers.appendElement(handler2);
    gHandlerService.store(handlerInfo);
  }
  // Set up our mailto handler test infrastructure.
  let mailtoHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("mailto");
  scrubMailtoHandlers(mailtoHandlerInfo);
  gOriginalPreferredMailHandler = mailtoHandlerInfo.preferredApplicationHandler;
  substituteWebHandlers(mailtoHandlerInfo);

  // Now do the same for pdf handler:
  let pdfHandlerInfo =
    HandlerServiceTestUtils.getHandlerInfo("application/pdf");
  // PDF doesn't have built-in web handlers, so no need to scrub.
  gOriginalPreferredPDFHandler = pdfHandlerInfo.preferredApplicationHandler;
  substituteWebHandlers(pdfHandlerInfo);

  appHandlerInitialized = TestUtils.topicObserved("app-handler-loaded");

  await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true });

  info("Preferences page opened on the general pane.");

  await gBrowser.selectedBrowser.contentWindow.promiseLoadHandlersList;
  info("Apps list loaded.");
});

/**
 * @param {MozBoxItem} itemToUse
 */
async function selectStandardOptions(itemToUse) {
  /**
   * @type {MozSelect}
   */
  const list = itemToUse.querySelector(".actionsMenu");
  /**
   * @param {MozOption} item
   */
  async function selectItemInPopup(item) {
    list.value = item.value;
    list.dispatchEvent(new CustomEvent("change"));
    await list.updateComplete;
    return item;
  }

  let itemType = itemToUse.getAttribute("type");
  // select one of our test cases:
  let handlerItem = list.querySelector(
    "moz-option[data-l10n-args*='Handler 1']"
  );
  await selectItemInPopup(handlerItem);
  let { preferredAction, alwaysAskBeforeHandling } =
    HandlerServiceTestUtils.getHandlerInfo(itemType);
  Assert.notEqual(
    preferredAction,
    Ci.nsIHandlerInfo.alwaysAsk,
    "Should have selected something other than 'always ask' (" + itemType + ")"
  );
  Assert.ok(
    !alwaysAskBeforeHandling,
    "Should have turned off asking before handling (" + itemType + ")"
  );

  // Test the alwaysAsk option
  let alwaysAskItem = list.querySelector(
    `moz-option[action="${Ci.nsIHandlerInfo.alwaysAsk}"]`
  );
  await selectItemInPopup(alwaysAskItem);
  Assert.equal(
    list.value,
    alwaysAskItem.value,
    "Should have selected always ask item (" + itemType + ")"
  );
  alwaysAskBeforeHandling =
    HandlerServiceTestUtils.getHandlerInfo(itemType).alwaysAskBeforeHandling;
  Assert.ok(
    alwaysAskBeforeHandling,
    "Should have turned on asking before handling (" + itemType + ")"
  );

  let useDefaultItem = list.getElementsByAttribute(
    "action",
    Ci.nsIHandlerInfo.useSystemDefault
  );
  useDefaultItem = useDefaultItem && useDefaultItem[0];
  if (useDefaultItem) {
    await selectItemInPopup(useDefaultItem);
    Assert.equal(
      list.value,
      useDefaultItem.value,
      "Should have selected 'use default' item (" + itemType + ")"
    );
    preferredAction =
      HandlerServiceTestUtils.getHandlerInfo(itemType).preferredAction;
    Assert.equal(
      preferredAction,
      Ci.nsIHandlerInfo.useSystemDefault,
      "Should have selected 'use default' (" + itemType + ")"
    );
  } else {
    // Whether there's a "use default" item depends on the OS, so it's not
    // possible to rely on it being the case or not.
    info("No 'Use default' item, so not testing (" + itemType + ")");
  }

  // Select a web app item.
  let webAppItems = Array.from(
    list.getElementsByAttribute("action", Ci.nsIHandlerInfo.useHelperApp)
  );
  webAppItems = webAppItems.filter(
    item => item.handlerApp instanceof Ci.nsIWebHandlerApp
  );
  Assert.equal(
    webAppItems.length,
    2,
    "Should have 2 web application handler. (" + itemType + ")"
  );
  Assert.notEqual(
    webAppItems[0].label,
    webAppItems[1].label,
    "Should have 2 different web app handlers"
  );
  let selectedItem = await selectItemInPopup(webAppItems[0]);

  // Test that the selected item label is the same as the label
  // of the menu item.
  let win = gBrowser.selectedBrowser.contentWindow;
  await win.document.l10n.translateFragment(selectedItem);
  await win.document.l10n.translateFragment(itemToUse);
  Assert.equal(
    selectedItem.value,
    list.value,
    "Should have selected correct item (" + itemType + ")"
  );

  // select the other web app item
  selectedItem = await selectItemInPopup(webAppItems[1]);

  // Test that the selected item label is the same as the label
  // of the menu item
  await win.document.l10n.translateFragment(selectedItem);
  await win.document.l10n.translateFragment(itemToUse);
  Assert.equal(
    selectedItem.value,
    list.value,
    "Should have selected correct item (" + itemType + ")"
  );
}

add_task(async function checkDropdownBehavior() {
  await appHandlerInitialized;

  let win = gBrowser.selectedBrowser.contentWindow;

  let container = win.document.getElementById("applicationsHandlersView");

  // First check a protocol handler item.
  let mailItem = container.querySelector("moz-box-item[type='mailto']");
  Assert.ok(mailItem, "mailItem is present in handlersView.");
  await selectStandardOptions(mailItem);

  // Then check a content menu item.
  let pdfItem = container.querySelector("moz-box-item[type='application/pdf']");
  Assert.ok(pdfItem, "pdfItem is present in handlersView.");
  await selectStandardOptions(pdfItem);
});
