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

"use strict";

const { AMBrowserExtensionsImport, AddonManager } = ChromeUtils.importESModule(
  "resource://gre/modules/AddonManager.sys.mjs"
);
const { AddonTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/AddonTestUtils.sys.mjs"
);
const { ChromeMigrationUtils } = ChromeUtils.importESModule(
  "resource:///modules/ChromeMigrationUtils.sys.mjs"
);
const { ChromeProfileMigrator } = ChromeUtils.importESModule(
  "resource:///modules/ChromeProfileMigrator.sys.mjs"
);

AddonTestUtils.init(this);
AddonTestUtils.overrideCertDB();
AddonTestUtils.createAppInfo(
  "xpcshell@tests.mozilla.org",
  "XPCShell",
  "42",
  "42"
);

const TEST_SERVER = AddonTestUtils.createHttpServer({ hosts: ["example.com"] });

const PROFILE = {
  id: "Default",
  name: "Person 1",
};

const IMPORTED_ADDON_1 = {
  name: "A Firefox extension",
  version: "1.0",
  browser_specific_settings: { gecko: { id: "some-ff@extension" } },
};

// Setup chrome user data path for all platforms.
ChromeMigrationUtils.getDataPath = () => {
  return Promise.resolve(
    do_get_file("Library/Application Support/Google/Chrome/").path
  );
};

const mockAddonRepository = ({ addons = [] } = {}) => {
  return {
    async getMappedAddons(browserID, extensionIDs) {
      Assert.equal(browserID, "chrome", "expected browser ID");
      // Sort extension IDs to have a predictable order.
      extensionIDs.sort();
      Assert.deepEqual(
        extensionIDs,
        ["fake-extension-1", "fake-extension-2"],
        "expected an array of 2 extension IDs"
      );
      return Promise.resolve(addons);
    },
  };
};

add_setup(async function setup() {
  await AddonTestUtils.promiseStartupManager();

  // Create a Firefox XPI that we can use during the import.
  const xpi = AddonTestUtils.createTempWebExtensionFile({
    manifest: IMPORTED_ADDON_1,
  });
  TEST_SERVER.registerFile(`/addons/addon-1.xpi`, xpi);

  // Override the `AddonRepository` in `AMBrowserExtensionsImport` with our own
  // mock so that we control the add-ons that are mapped to the list of Chrome
  // extension IDs.
  const addons = [
    {
      id: IMPORTED_ADDON_1.browser_specific_settings.gecko.id,
      name: IMPORTED_ADDON_1.name,
      version: IMPORTED_ADDON_1.version,
      sourceURI: Services.io.newURI(`http://example.com/addons/addon-1.xpi`),
      icons: {},
    },
  ];
  AMBrowserExtensionsImport._addonRepository = mockAddonRepository({ addons });

  registerCleanupFunction(() => {
    // Clear the add-on repository override.
    AMBrowserExtensionsImport._addonRepository = null;
  });
});

add_task(
  { pref_set: [["browser.migrate.chrome.extensions.enabled", true]] },
  async function test_import_extensions() {
    const migrator = await MigrationUtils.getMigrator("chrome");
    Assert.ok(
      await migrator.isSourceAvailable(),
      "Sanity check the source exists"
    );

    let promiseTopic = TestUtils.topicObserved(
      "webextension-imported-addons-pending"
    );
    await promiseMigration(
      migrator,
      MigrationUtils.resourceTypes.EXTENSIONS,
      PROFILE,
      true
    );
    await promiseTopic;
    // When this property is `true`, the UI should show a badge on the appmenu
    // button, and the user can finalize the import later.
    Assert.ok(
      AMBrowserExtensionsImport.canCompleteOrCancelInstalls,
      "expected some add-ons to have been imported"
    );

    // Let's actually complete the import programatically below.
    promiseTopic = TestUtils.topicObserved(
      "webextension-imported-addons-complete"
    );
    await AMBrowserExtensionsImport.completeInstalls();
    await Promise.all([
      AddonTestUtils.promiseInstallEvent("onInstallEnded"),
      promiseTopic,
    ]);

    // The add-on should be installed and therefore it can be uninstalled.
    const addon = await AddonManager.getAddonByID(
      IMPORTED_ADDON_1.browser_specific_settings.gecko.id
    );
    await addon.uninstall();
  }
);

/**
 * Test that, for now at least, the extension resource type is only made
 * available for Chrome and none of the derivitive browsers.
 */
add_task(
  { pref_set: [["browser.migrate.chrome.extensions.enabled", true]] },
  async function test_only_chrome_migrates_extensions() {
    for (const key of MigrationUtils.availableMigratorKeys) {
      let migrator = await MigrationUtils.getMigrator(key);

      if (migrator instanceof ChromeProfileMigrator && key != "chrome") {
        info("Testing migrator with key " + key);
        Assert.ok(
          await migrator.isSourceAvailable(),
          "First check the source exists"
        );
        let resourceTypes = await migrator.getMigrateData(PROFILE);
        Assert.ok(
          !(resourceTypes & MigrationUtils.resourceTypes.EXTENSIONS),
          "Should not offer the extension resource type"
        );
      }
    }
  }
);
