/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { Log } from "resource://gre/modules/Log.sys.mjs";
import { AddonStudies } from "resource://normandy/lib/AddonStudies.sys.mjs";
import { PreferenceExperiments } from "resource://normandy/lib/PreferenceExperiments.sys.mjs";
import { RecipeRunner } from "resource://normandy/lib/RecipeRunner.sys.mjs";

const BOOTSTRAP_LOGGER_NAME = "app.normandy.bootstrap";

const PREF_PREFIX = "app.normandy";
const LEGACY_PREF_PREFIX = "extensions.shield-recipe-client";
const PREF_LOGGING_LEVEL = "app.normandy.logging.level";
const PREF_MIGRATIONS_APPLIED = "app.normandy.migrationsApplied";
const PREF_OPTOUTSTUDIES_ENABLED = "app.shield.optoutstudies.enabled";

// Logging
const log = Log.repository.getLogger(BOOTSTRAP_LOGGER_NAME);
log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
log.level = Services.prefs.getIntPref(PREF_LOGGING_LEVEL, Log.Level.Warn);

export const NormandyMigrations = {
  async applyAll() {
    let migrationsApplied = Services.prefs.getIntPref(
      PREF_MIGRATIONS_APPLIED,
      0
    );

    for (let i = migrationsApplied; i < this.migrations.length; i++) {
      await this.applyOne(i);
      migrationsApplied++;
      Services.prefs.setIntPref(PREF_MIGRATIONS_APPLIED, migrationsApplied);
    }
  },

  async applyOne(id) {
    const migration = this.migrations[id];
    log.debug(`Running Normandy migration ${migration.name}`);
    await migration();
  },

  migrations: [
    migrateShieldPrefs,
    migrateStudiesEnabledWithoutHealthReporting,
    AddonStudies.migrations
      .migration01AddonStudyFieldsToSlugAndUserFacingFields,
    PreferenceExperiments.migrations.migration01MoveExperiments,
    PreferenceExperiments.migrations.migration02MultiPreference,
    PreferenceExperiments.migrations.migration03AddActionName,
    PreferenceExperiments.migrations.migration04RenameNameToSlug,
    RecipeRunner.migrations.migration01RemoveOldRecipesCollection,
    AddonStudies.migrations.migration02RemoveOldAddonStudyAction,
    migrateRemoveLastBuildIdPref,
    PreferenceExperiments.migrations.migration05RemoveOldAction,
    PreferenceExperiments.migrations.migration06TrackOverriddenPrefs,
  ],
};

function migrateShieldPrefs() {
  const legacyBranch = Services.prefs.getBranch(LEGACY_PREF_PREFIX + ".");
  const newBranch = Services.prefs.getBranch(PREF_PREFIX + ".");

  for (const prefName of legacyBranch.getChildList("")) {
    const legacyPrefType = legacyBranch.getPrefType(prefName);
    const newPrefType = newBranch.getPrefType(prefName);

    // If new preference exists and is not the same as the legacy pref, skip it
    if (
      newPrefType !== Services.prefs.PREF_INVALID &&
      newPrefType !== legacyPrefType
    ) {
      log.error(
        `Error migrating normandy pref ${prefName}; pref type does not match.`
      );
      continue;
    }

    // Now move the value over. If it matches the default, this will be a no-op
    switch (legacyPrefType) {
      case Services.prefs.PREF_STRING:
        newBranch.setCharPref(prefName, legacyBranch.getCharPref(prefName));
        break;

      case Services.prefs.PREF_INT:
        newBranch.setIntPref(prefName, legacyBranch.getIntPref(prefName));
        break;

      case Services.prefs.PREF_BOOL:
        newBranch.setBoolPref(prefName, legacyBranch.getBoolPref(prefName));
        break;

      case Services.prefs.PREF_INVALID:
        // This should never happen.
        log.error(
          `Error migrating pref ${prefName}; pref type is invalid (${legacyPrefType}).`
        );
        break;

      default:
        // This should never happen either.
        log.error(
          `Error getting startup pref ${prefName}; unknown value type ${legacyPrefType}.`
        );
    }

    legacyBranch.clearUserPref(prefName);
  }
}

/**
 * Migration to handle moving the studies opt-out pref from under the health
 * report upload pref to an independent pref.
 *
 * If the pref was set to true and the health report upload pref was set
 * to true then the pref should stay true. Otherwise set it to false.
 */
function migrateStudiesEnabledWithoutHealthReporting() {
  const optOutStudiesEnabled = Services.prefs.getBoolPref(
    PREF_OPTOUTSTUDIES_ENABLED,
    false
  );
  const healthReportUploadEnabled = Services.prefs.getBoolPref(
    "datareporting.healthreport.uploadEnabled",
    false
  );
  Services.prefs.setBoolPref(
    PREF_OPTOUTSTUDIES_ENABLED,
    optOutStudiesEnabled && healthReportUploadEnabled
  );
}

/**
 * Tracking last build ID is now done by comparing Services.appinfo.appBuildID
 * and Services.appinfo.lastAppBuildID. Remove the manual tracking.
 */
function migrateRemoveLastBuildIdPref() {
  Services.prefs.clearUserPref("app.normandy.last_seen_buildid");
}
