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

"use strict";

// We're in an xpcshell test but have an eslint browser test env applied;
// We definitely do need to manually import CustomizableUI.
// eslint-disable-next-line mozilla/no-redeclare-with-import-autofix
const { CustomizableUI } = ChromeUtils.importESModule(
  "resource:///modules/CustomizableUI.sys.mjs"
);

do_get_profile();

// Make Cu.isInAutomation true. This is necessary so that we can use
// CustomizableUIInternal.
Services.prefs.setBoolPref(
  "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer",
  true
);

const CustomizableUIInternal = CustomizableUI.getTestOnlyInternalProp(
  "CustomizableUIInternal"
);

// Migration 19 was the Unified Extensions migration version introduced
// in 109, so we'll run tests by artificially setting the migration version
// to one value earlier.
const PRIOR_MIGRATION_VERSION = 18;

/**
 * Writes customization state into CustomizableUI and then performs the forward migration
 * for Unified Extensions.
 *
 * @param {object|null} stateObj An object that will be structure-cloned and
 *   written into CustomizableUI's internal `gSavedState` state variable. Should
 *   not include the currentVersion property, as this will be set automatically by
 *   function if stateObj is not null.
 * @returns {object}
 *   the saved state object (minus the currentVersion property).
 */
function migrateForward(stateObj) {
  // We make sure to use structuredClone here so that we don't end up comparing
  // SAVED_STATE against itself.
  let stateToSave = structuredClone(stateObj);
  if (stateToSave) {
    stateToSave.currentVersion = PRIOR_MIGRATION_VERSION;
  }

  CustomizableUI.setTestOnlyInternalProp("gSavedState", stateToSave);
  CustomizableUIInternal._updateForNewVersion();

  let migratedState = CustomizableUI.getTestOnlyInternalProp("gSavedState");
  if (migratedState) {
    delete migratedState.currentVersion;
  }
  return migratedState;
}

/**
 * Test that attempting a migration on a new profile with no saved
 * state exits safely.
 */
add_task(async function test_no_saved_state() {
  let migratedState = migrateForward(null);

  Assert.deepEqual(
    migratedState,
    null,
    "gSavedState should not have been modified"
  );
});

/**
 * Test that attempting a migration on a new profile with no saved
 * state exits safely.
 */
add_task(async function test_no_saved_placements() {
  let migratedState = migrateForward({});

  Assert.deepEqual(
    migratedState,
    {},
    "gSavedState should not have been modified"
  );
});

/**
 * Test that placements that don't involve any extension buttons are
 * not changed during the migration.
 */
add_task(async function test_no_extensions() {
  const SAVED_STATE = {
    placements: {
      "nav-bar": [
        "back-button",
        "forward-button",
        "spring",
        "urlbar-container",
        "save-to-pocket-button",
      ],
      "toolbar-menubar": [
        "home-button",
        "menubar-items",
        "spring",
        "downloads-button",
      ],
      TabsToolbar: [
        "firefox-view-button",
        "tabbrowser-tabs",
        "new-tab-button",
        "alltabs-button",
        "developer-button",
      ],
      PersonalToolbar: ["personal-bookmarks", "fxa-toolbar-menu-button"],
      "widget-overflow-fixed-list": ["privatebrowsing-button", "panic-button"],
    },
  };

  // ADDONS_AREA should end up with an empty array as its set of placements.
  const EXPECTED_STATE = structuredClone(SAVED_STATE);
  EXPECTED_STATE.placements[CustomizableUI.AREA_ADDONS] = [];

  let migratedState = migrateForward(SAVED_STATE);

  Assert.deepEqual(
    migratedState,
    EXPECTED_STATE,
    "Got the expected state after the migration."
  );
});

/**
 * Test that if there's an existing set of items in CustomizableUI.AREA_ADDONS,
 * and no extension buttons to migrate from the overflow menu, then we don't
 * change the state at all.
 */
add_task(async function test_existing_browser_actions_no_movement() {
  const SAVED_STATE = {
    placements: {
      "nav-bar": [
        "back-button",
        "forward-button",
        "spring",
        "urlbar-container",
        "save-to-pocket-button",
      ],
      "toolbar-menubar": [
        "home-button",
        "menubar-items",
        "spring",
        "downloads-button",
      ],
      TabsToolbar: [
        "firefox-view-button",
        "tabbrowser-tabs",
        "new-tab-button",
        "alltabs-button",
        "developer-button",
      ],
      PersonalToolbar: ["personal-bookmarks", "fxa-toolbar-menu-button"],
      "widget-overflow-fixed-list": ["privatebrowsing-button", "panic-button"],
      "unified-extensions-area": ["ext0-browser-action", "ext1-browser-action"],
    },
  };

  let migratedState = migrateForward(SAVED_STATE);

  Assert.deepEqual(
    migratedState,
    SAVED_STATE,
    "The saved state should not have changed after migration."
  );
});

/**
 * Test that we can migrate extension buttons out from the overflow panel
 * into the addons panel.
 */
add_task(async function test_migrate_extension_buttons() {
  const SAVED_STATE = {
    placements: {
      "nav-bar": [
        "back-button",
        "forward-button",
        "spring",
        "urlbar-container",
        "save-to-pocket-button",
      ],
      "toolbar-menubar": [
        "home-button",
        "menubar-items",
        "spring",
        "downloads-button",
      ],
      TabsToolbar: [
        "firefox-view-button",
        "tabbrowser-tabs",
        "new-tab-button",
        "alltabs-button",
        "developer-button",
      ],
      PersonalToolbar: ["personal-bookmarks", "fxa-toolbar-menu-button"],
      "widget-overflow-fixed-list": [
        "ext0-browser-action",
        "privatebrowsing-button",
        "ext1-browser-action",
        "panic-button",
        "ext2-browser-action",
      ],
    },
  };
  const EXPECTED_STATE = structuredClone(SAVED_STATE);
  EXPECTED_STATE.placements[CustomizableUI.AREA_FIXED_OVERFLOW_PANEL] = [
    "privatebrowsing-button",
    "panic-button",
  ];
  EXPECTED_STATE.placements[CustomizableUI.AREA_ADDONS] = [
    "ext0-browser-action",
    "ext1-browser-action",
    "ext2-browser-action",
  ];

  let migratedState = migrateForward(SAVED_STATE);

  Assert.deepEqual(
    migratedState,
    EXPECTED_STATE,
    "The saved state should not have changed after migration."
  );
});

/**
 * Test that we won't overwrite existing placements within the addons panel
 * if we migrate things over from the overflow panel. We'll prepend the
 * migrated items to the addons panel instead.
 */
add_task(async function test_migrate_extension_buttons_no_overwrite() {
  const SAVED_STATE = {
    placements: {
      "nav-bar": [
        "back-button",
        "forward-button",
        "spring",
        "urlbar-container",
        "save-to-pocket-button",
      ],
      "toolbar-menubar": [
        "home-button",
        "menubar-items",
        "spring",
        "downloads-button",
      ],
      TabsToolbar: [
        "firefox-view-button",
        "tabbrowser-tabs",
        "new-tab-button",
        "alltabs-button",
        "developer-button",
      ],
      PersonalToolbar: ["personal-bookmarks", "fxa-toolbar-menu-button"],
      "widget-overflow-fixed-list": [
        "ext0-browser-action",
        "privatebrowsing-button",
        "ext1-browser-action",
        "panic-button",
        "ext2-browser-action",
      ],
      "unified-extensions-area": ["ext3-browser-action", "ext4-browser-action"],
    },
  };
  const EXPECTED_STATE = structuredClone(SAVED_STATE);
  EXPECTED_STATE.placements[CustomizableUI.AREA_FIXED_OVERFLOW_PANEL] = [
    "privatebrowsing-button",
    "panic-button",
  ];
  EXPECTED_STATE.placements[CustomizableUI.AREA_ADDONS] = [
    "ext0-browser-action",
    "ext1-browser-action",
    "ext2-browser-action",
    "ext3-browser-action",
    "ext4-browser-action",
  ];

  let migratedState = migrateForward(SAVED_STATE);

  Assert.deepEqual(
    migratedState,
    EXPECTED_STATE,
    "The saved state should not have changed after migration."
  );
});

/**
 * Test that extension buttons from areas other than the overflow panel
 * won't be moved.
 */
add_task(async function test_migrate_extension_buttons_elsewhere() {
  const SAVED_STATE = {
    placements: {
      "nav-bar": [
        "back-button",
        "ext0-browser-action",
        "forward-button",
        "ext1-browser-action",
        "spring",
        "ext2-browser-action",
        "urlbar-container",
        "ext3-browser-action",
        "save-to-pocket-button",
        "ext4-browser-action",
      ],
      "toolbar-menubar": [
        "home-button",
        "ext5-browser-action",
        "menubar-items",
        "ext6-browser-action",
        "spring",
        "ext7-browser-action",
        "downloads-button",
        "ext8-browser-action",
      ],
      TabsToolbar: [
        "firefox-view-button",
        "ext9-browser-action",
        "tabbrowser-tabs",
        "ext10-browser-action",
        "new-tab-button",
        "ext11-browser-action",
        "alltabs-button",
        "ext12-browser-action",
        "developer-button",
        "ext13-browser-action",
      ],
      PersonalToolbar: [
        "personal-bookmarks",
        "ext14-browser-action",
        "fxa-toolbar-menu-button",
        "ext15-browser-action",
      ],
      "widget-overflow-fixed-list": [
        "ext16-browser-action",
        "privatebrowsing-button",
        "ext17-browser-action",
        "panic-button",
        "ext18-browser-action",
      ],
    },
  };
  const EXPECTED_STATE = structuredClone(SAVED_STATE);
  EXPECTED_STATE.placements[CustomizableUI.AREA_FIXED_OVERFLOW_PANEL] = [
    "privatebrowsing-button",
    "panic-button",
  ];
  EXPECTED_STATE.placements[CustomizableUI.AREA_ADDONS] = [
    "ext16-browser-action",
    "ext17-browser-action",
    "ext18-browser-action",
  ];

  let migratedState = migrateForward(SAVED_STATE);

  Assert.deepEqual(
    migratedState,
    EXPECTED_STATE,
    "The saved state should not have changed after migration."
  );
});
