const { AddonTestUtils } = ChromeUtils.import(
  "resource://testing-common/AddonTestUtils.jsm"
);

AddonTestUtils.initMochitest(this);

let gManagerWindow;
let gCategoryUtilities;

const TELEMETRY_METHODS = ["action", "link", "view"];
const addonId = "extension@mochi.test";

registerCleanupFunction(() => {
  // AddonTestUtils with open_manager cause this reference to be maintained and creates a leak.
  gManagerWindow = null;
});

add_task(function setupPromptService() {
  let promptService = mockPromptService();
  promptService._response = 0; // Accept dialogs.
});

async function installTheme() {
  let id = "theme@mochi.test";
  let extension = ExtensionTestUtils.loadExtension({
    manifest: {
      applications: { gecko: { id } },
      manifest_version: 2,
      name: "atheme",
      description: "wow. such theme.",
      author: "Pixel Pusher",
      version: "1",
      theme: {},
    },
    useAddonManager: "temporary",
  });
  await extension.startup();
  return extension;
}

async function installExtension(manifest = {}) {
  let extension = ExtensionTestUtils.loadExtension({
    manifest: {
      applications: { gecko: { id: addonId } },
      manifest_version: 2,
      name: "extension",
      description: "wow. such extension.",
      author: "Code Pusher",
      version: "1",
      chrome_url_overrides: { newtab: "new.html" },
      options_ui: { page: "options.html", open_in_tab: true },
      browser_action: { default_popup: "action.html" },
      page_action: { default_popup: "action.html" },
      ...manifest,
    },
    files: {
      "new.html": "<h1>yo</h1>",
      "options.html": "<h1>options</h1>",
      "action.html": "<h1>do something</h1>",
    },
    useAddonManager: "permanent",
  });
  await extension.startup();
  return extension;
}

function openDetailView(doc, id) {
  let card = getAddonCard(doc.defaultView, id);
  card.querySelector('[action="expand"]').click();
}

async function enableAndDisable(doc, row) {
  let toggleButton = row.querySelector('[action="toggle-disabled"]');
  let disabled = BrowserTestUtils.waitForEvent(row, "update");
  toggleButton.click();
  await disabled;
  let enabled = BrowserTestUtils.waitForEvent(row, "update");
  toggleButton.click();
  await enabled;
}

async function removeAddonAndUndo(doc, row) {
  let id = row.addon.id;
  let started = AddonTestUtils.promiseWebExtensionStartup(id);
  let removed = BrowserTestUtils.waitForEvent(row, "remove");
  row.querySelector('[action="remove"]').click();
  await removed;

  let undoBanner = doc.querySelector(`message-bar[addon-id="${row.addon.id}"]`);
  undoBanner.querySelector('[action="undo"]').click();
  const win = doc.defaultView;
  await TestUtils.waitForCondition(() => getAddonCard(win, row.addon.id));
  await started;
}

async function openPrefs(doc, row) {
  row.querySelector('[action="preferences"]').click();
}

function changeAutoUpdates(doc) {
  let row = doc.querySelector(".addon-detail-row-updates");
  let checked = row.querySelector(":checked");
  is(checked.value, "1", "Use default is selected");
  row.querySelector('[value="0"]').click();
  row.querySelector('[action="update-check"]').click();
  row.querySelector('[value="2"]').click();
  row.querySelector('[value="1"]').click();
}

function clickLinks(doc) {
  let links = ["author", "homepage", "rating"];
  for (let linkType of links) {
    doc.querySelector(`.addon-detail-row-${linkType} a`).click();
  }
}

async function init(startPage) {
  gManagerWindow = await open_manager(null);
  gCategoryUtilities = new CategoryUtilities(gManagerWindow);

  // When about:addons initially loads it will load the last view that
  // was open. If that's different than startPage, then clear the events
  // so that we can reliably test them.
  if (gCategoryUtilities.selectedCategory != startPage) {
    Services.telemetry.clearEvents();
  }

  await gCategoryUtilities.openType(startPage);

  return gManagerWindow.document;
}

/* Test functions start here. */

add_setup(async function() {
  // Clear out any telemetry data that existed before this file is run.
  Services.telemetry.clearEvents();
});

add_task(async function testBasicViewTelemetry() {
  let addons = await Promise.all([installTheme(), installExtension()]);
  let doc = await init("discover");

  await gCategoryUtilities.openType("theme");
  openDetailView(doc, "theme@mochi.test");
  await wait_for_view_load(gManagerWindow);

  await gCategoryUtilities.openType("extension");
  openDetailView(doc, "extension@mochi.test");
  await wait_for_view_load(gManagerWindow);

  assertAboutAddonsTelemetryEvents(
    [
      [
        "addonsManager",
        "view",
        "aboutAddons",
        "discover",
        { taar_enabled: "0" },
      ],
      ["addonsManager", "view", "aboutAddons", "list", { type: "theme" }],
      [
        "addonsManager",
        "view",
        "aboutAddons",
        "detail",
        { type: "theme", addonId: "theme@mochi.test" },
      ],
      ["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
      [
        "addonsManager",
        "view",
        "aboutAddons",
        "detail",
        { type: "extension", addonId: "extension@mochi.test" },
      ],
    ],
    { methods: ["view"] }
  );

  await close_manager(gManagerWindow);
  await Promise.all(addons.map(addon => addon.unload()));
});

add_task(async function testExtensionEvents() {
  let addon = await installExtension();
  let type = "extension";
  let doc = await init("extension");
  let win = doc.defaultView;

  // Check/clear the current telemetry.
  assertAboutAddonsTelemetryEvents(
    [["addonsManager", "view", "aboutAddons", "list", { type: "extension" }]],
    { methods: ["view"] }
  );

  let row = getAddonCard(win, addonId);

  // Check disable/enable.
  await enableAndDisable(doc, row);
  assertAboutAddonsTelemetryEvents(
    [
      [
        "addonsManager",
        "action",
        "aboutAddons",
        null,
        { action: "disable", addonId, type, view: "list" },
      ],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        null,
        { action: "enable", addonId, type, view: "list" },
      ],
    ],
    { methods: ["action"] }
  );

  // Check remove/undo.
  await removeAddonAndUndo(doc, row);
  let uninstallValue = "accepted";
  assertAboutAddonsTelemetryEvents(
    [
      [
        "addonsManager",
        "action",
        "aboutAddons",
        uninstallValue,
        { action: "uninstall", addonId, type, view: "list" },
      ],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        null,
        { action: "undo", addonId, type, view: "list" },
      ],
    ],
    { methods: ["action"] }
  );

  // Open the preferences page.
  let waitForNewTab = BrowserTestUtils.waitForNewTab(gBrowser);
  // Find the row again since it was modified on uninstall.
  row = getAddonCard(win, addonId);
  await openPrefs(doc, row);
  BrowserTestUtils.removeTab(await waitForNewTab);
  assertAboutAddonsTelemetryEvents(
    [
      [
        "addonsManager",
        "action",
        "aboutAddons",
        "external",
        { action: "preferences", type, addonId, view: "list" },
      ],
    ],
    { methods: ["action"] }
  );

  // Go to the detail view.
  openDetailView(doc, addonId);
  await wait_for_view_load(gManagerWindow);
  assertAboutAddonsTelemetryEvents(
    [["addonsManager", "view", "aboutAddons", "detail", { type, addonId }]],
    { methods: ["view"] }
  );

  // Check updates.
  changeAutoUpdates(doc);
  assertAboutAddonsTelemetryEvents(
    [
      [
        "addonsManager",
        "action",
        "aboutAddons",
        "",
        { action: "setAddonUpdate", type, addonId, view: "detail" },
      ],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        null,
        { action: "checkForUpdate", type, addonId, view: "detail" },
      ],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        "enabled",
        { action: "setAddonUpdate", type, addonId, view: "detail" },
      ],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        "default",
        { action: "setAddonUpdate", type, addonId, view: "detail" },
      ],
    ],
    { methods: ["action"] }
  );

  // These links don't actually have a URL, so they don't open a tab. They're only
  // shown when there is a URL though.
  clickLinks(doc);

  // The support button will open a new tab.
  waitForNewTab = BrowserTestUtils.waitForNewTab(gBrowser);
  doc.getElementById("help-button").click();
  BrowserTestUtils.removeTab(await waitForNewTab);

  // Check that the preferences button includes the view.
  waitForNewTab = BrowserTestUtils.waitForNewTab(gBrowser);
  row = getAddonCard(win, addonId);
  await openPrefs(doc, row);
  BrowserTestUtils.removeTab(await waitForNewTab);

  assertAboutAddonsTelemetryEvents(
    [
      ["addonsManager", "link", "aboutAddons", "author", { view: "detail" }],
      ["addonsManager", "link", "aboutAddons", "homepage", { view: "detail" }],
      ["addonsManager", "link", "aboutAddons", "rating", { view: "detail" }],
      ["addonsManager", "link", "aboutAddons", "support", { view: "detail" }],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        "external",
        { action: "preferences", type, addonId, view: "detail" },
      ],
    ],
    { methods: ["action", "link"] }
  );

  // Update the preferences and check that inline changes.
  await gCategoryUtilities.openType("extension");
  let upgraded = await installExtension({
    options_ui: { page: "options.html" },
    version: "2",
  });
  row = getAddonCard(win, addonId);
  await openPrefs(doc, row);
  await wait_for_view_load(gManagerWindow);

  assertAboutAddonsTelemetryEvents(
    [
      ["addonsManager", "view", "aboutAddons", "list", { type }],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        "inline",
        { action: "preferences", type, addonId, view: "list" },
      ],
      [
        "addonsManager",
        "view",
        "aboutAddons",
        "detail",
        { type: "extension", addonId: "extension@mochi.test" },
      ],
    ],
    { methods: ["action", "view"] }
  );

  await close_manager(gManagerWindow);
  await addon.unload();
  await upgraded.unload();
});

add_task(async function testGeneralActions() {
  let win = await loadInitialView("extension");
  info("Loaded");

  let doc = win.document;
  let pageOptionsButton = doc.querySelector('[action="page-options"]');
  let menu = doc.querySelector("#page-options panel-list");
  let checkForUpdates = menu.querySelector('[action="check-for-updates"]');
  let recentUpdates = menu.querySelector('[action="view-recent-updates"]');
  let updatePolicy = menu.querySelector('[action="set-update-automatically"]');
  let resetUpdatePolicy = menu.querySelector('[action="reset-update-states"]');
  let debugAddons = menu.querySelector('[action="debug-addons"]');
  let manageShortcuts = menu.querySelector('[action="manage-shortcuts"]');

  async function clickInGearMenu(item) {
    info(`Opening menu to click ${item.getAttribute("action")}`);
    let shown = BrowserTestUtils.waitForEvent(menu, "shown");
    // This should perform a click on the button to ensure that works. Other
    // tests might just open the menu directly, or click items when it's closed.
    EventUtils.synthesizeMouseAtCenter(pageOptionsButton, {}, win);
    await shown;
    info(`Clicking ${item.getAttribute("action")}`);
    item.click();
  }

  await clickInGearMenu(checkForUpdates);
  let recentUpdatesLoaded = waitForViewLoad(win);
  await clickInGearMenu(recentUpdates);
  await recentUpdatesLoaded;
  await clickInGearMenu(updatePolicy);
  await clickInGearMenu(updatePolicy);
  await clickInGearMenu(resetUpdatePolicy);

  // Check shortcuts view.
  let shortcutsLoaded = waitForViewLoad(win);
  await clickInGearMenu(manageShortcuts);
  await shortcutsLoaded;
  await clickInGearMenu(checkForUpdates);

  let waitForNewTab = BrowserTestUtils.waitForNewTab(gBrowser);
  await clickInGearMenu(debugAddons);
  info("Waiting for about:debugging tab");
  let tab = await waitForNewTab;
  BrowserTestUtils.removeTab(tab);

  waitForNewTab = BrowserTestUtils.waitForNewTab(gBrowser);
  let searchBox = doc.querySelector("search-addons").input.inputField;
  searchBox.value = "something";
  searchBox.focus();
  EventUtils.synthesizeKey("KEY_Enter", {}, win);
  info("Waiting for AMO search tab");
  tab = await waitForNewTab;
  BrowserTestUtils.removeTab(tab);

  assertAboutAddonsTelemetryEvents(
    [
      ["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        null,
        { action: "checkForUpdates", view: "list" },
      ],
      ["addonsManager", "view", "aboutAddons", "updates", { type: "recent" }],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        "default,enabled",
        { action: "setUpdatePolicy", view: "updates" },
      ],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        "enabled",
        { action: "setUpdatePolicy", view: "updates" },
      ],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        null,
        { action: "resetUpdatePolicy", view: "updates" },
      ],
      ["addonsManager", "view", "aboutAddons", "shortcuts"],
      [
        "addonsManager",
        "action",
        "aboutAddons",
        null,
        { action: "checkForUpdates", view: "shortcuts" },
      ],
      [
        "addonsManager",
        "link",
        "aboutAddons",
        "about:debugging",
        { view: "shortcuts" },
      ],
      [
        "addonsManager",
        "link",
        "aboutAddons",
        "search",
        { view: "shortcuts", type: "shortcuts" },
      ],
    ],
    { methods: TELEMETRY_METHODS }
  );

  await closeView(win);

  assertAboutAddonsTelemetryEvents([]);
});

add_task(async function testPreferencesLink() {
  assertAboutAddonsTelemetryEvents([]);

  let doc = await init("theme");

  // Open the about:preferences page from about:addons.
  let waitForNewTab = BrowserTestUtils.waitForNewTab(
    gBrowser,
    "about:preferences",
    true
  );
  doc.getElementById("preferencesButton").click();
  let tab = await waitForNewTab;
  let getAddonsButton = () =>
    tab.linkedBrowser.contentDocument.getElementById("addonsButton");

  // Open the about:addons page from about:preferences.
  getAddonsButton().click();

  // Close the about:preferences tab.
  BrowserTestUtils.removeTab(tab);

  TelemetryTestUtils.assertEvents(
    [
      ["addonsManager", "view", "aboutAddons", "list", { type: "theme" }],
      [
        "addonsManager",
        "link",
        "aboutAddons",
        "about:preferences",
        { view: "list" },
      ],
      ["addonsManager", "link", "aboutPreferences", "about:addons"],
    ],
    {
      category: "addonsManager",
      method: /^(link|view)$/,
    }
  );

  await close_manager(gManagerWindow);
});
