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

"use strict";

const { SmartTabGroupingManager, SMART_TAB_GROUPING_CONFIG } =
  ChromeUtils.importESModule(
    "moz-src:///browser/components/tabbrowser/SmartTabGrouping.sys.mjs"
  );

const { sinon } = ChromeUtils.importESModule(
  "resource://testing-common/Sinon.sys.mjs"
);

const { MLUninstallService } = ChromeUtils.importESModule(
  "chrome://global/content/ml/Utils.sys.mjs"
);

const PREF_ENABLED = "browser.tabs.groups.smart.enabled";
const PREF_USER_ENABLED = "browser.tabs.groups.smart.userEnabled";
const PREF_OPTIN = "browser.tabs.groups.smart.optin";

function clearSTGPrefs() {
  for (const pref of [PREF_ENABLED, PREF_USER_ENABLED, PREF_OPTIN]) {
    if (Services.prefs.prefHasUserValue(pref)) {
      Services.prefs.clearUserPref(pref);
    }
  }
}

// Ensure we actually create a user value even if the default is already the same.
function setUserBoolPrefDifferentFromDefault(prefName) {
  let defaultValue = Services.prefs.getDefaultBranch("").getBoolPref(prefName);
  Services.prefs.setBoolPref(prefName, !defaultValue);
}

registerCleanupFunction(() => {
  clearSTGPrefs();
  sinon.restore();
});

add_task(function test_id_is_feature_id() {
  Assert.equal(
    SmartTabGroupingManager.id,
    "smart-tab-grouping",
    "SmartTabGroupingManager.id should be the feature id"
  );
});

add_task(function test_isEnabled_requires_all_prefs() {
  clearSTGPrefs();

  Assert.ok(
    !SmartTabGroupingManager.isEnabled,
    "With no user prefs set, isEnabled should be false"
  );

  Services.prefs.setBoolPref(PREF_ENABLED, true);
  Services.prefs.setBoolPref(PREF_USER_ENABLED, true);
  Services.prefs.setBoolPref(PREF_OPTIN, true);

  Assert.ok(
    SmartTabGroupingManager.isEnabled,
    "isEnabled should be true when all three prefs are true"
  );

  Services.prefs.setBoolPref(PREF_OPTIN, false);
  Assert.ok(
    !SmartTabGroupingManager.isEnabled,
    "isEnabled should be false if optin=false"
  );

  Services.prefs.setBoolPref(PREF_OPTIN, true);
  Services.prefs.setBoolPref(PREF_USER_ENABLED, false);
  Assert.ok(
    !SmartTabGroupingManager.isEnabled,
    "isEnabled should be false if userEnabled=false"
  );

  Services.prefs.setBoolPref(PREF_USER_ENABLED, true);
  Services.prefs.setBoolPref(PREF_ENABLED, false);
  Assert.ok(
    !SmartTabGroupingManager.isEnabled,
    "isEnabled should be false if enabled=false"
  );
});

add_task(async function test_enable_sets_all_prefs_true() {
  clearSTGPrefs();

  await SmartTabGroupingManager.enable();

  Assert.equal(
    Services.prefs.getBoolPref(PREF_ENABLED, false),
    true,
    "enable() should set browser.tabs.groups.smart.enabled=true"
  );
  Assert.equal(
    Services.prefs.getBoolPref(PREF_USER_ENABLED, false),
    true,
    "enable() should set browser.tabs.groups.smart.userEnabled=true"
  );
  Assert.equal(
    Services.prefs.getBoolPref(PREF_OPTIN, false),
    true,
    "enable() should set browser.tabs.groups.smart.optin=true"
  );

  Assert.ok(
    SmartTabGroupingManager.isEnabled,
    "After enable(), isEnabled should be true"
  );
});

add_task(async function test_reset_clears_user_prefs_and_uninstalls_models() {
  clearSTGPrefs();

  // Force actual user values even if defaults match.
  setUserBoolPrefDifferentFromDefault(PREF_ENABLED);
  setUserBoolPrefDifferentFromDefault(PREF_USER_ENABLED);
  setUserBoolPrefDifferentFromDefault(PREF_OPTIN);

  Assert.ok(
    Services.prefs.prefHasUserValue(PREF_ENABLED),
    "Sanity check: enabled has a user value before reset()"
  );
  Assert.ok(
    Services.prefs.prefHasUserValue(PREF_USER_ENABLED),
    "Sanity check: userEnabled has a user value before reset()"
  );
  Assert.ok(
    Services.prefs.prefHasUserValue(PREF_OPTIN),
    "Sanity check: optin has a user value before reset()"
  );

  const uninstallStub = sinon.stub(MLUninstallService, "uninstall").resolves();

  await SmartTabGroupingManager.makeAvailable();

  Assert.ok(
    !Services.prefs.prefHasUserValue(PREF_ENABLED),
    "reset() should clear user pref for enabled"
  );
  Assert.ok(
    !Services.prefs.prefHasUserValue(PREF_USER_ENABLED),
    "reset() should clear user pref for userEnabled"
  );
  Assert.ok(
    !Services.prefs.prefHasUserValue(PREF_OPTIN),
    "reset() should clear user pref for optin"
  );

  Assert.ok(
    uninstallStub.calledOnce,
    "reset() should uninstall ML engine files via MLUninstallService.uninstall()"
  );

  const expectedEngineIds = [
    SMART_TAB_GROUPING_CONFIG.topicGeneration.engineId,
    SMART_TAB_GROUPING_CONFIG.embedding.engineId,
  ].sort();

  const uninstallArgs = uninstallStub.getCall(0).args[0];
  Assert.deepEqual(
    (uninstallArgs.engineIds || []).slice().sort(),
    expectedEngineIds,
    "reset() should uninstall files for both STG engines"
  );
  Assert.equal(
    uninstallArgs.actor,
    "SmartTabGrouping",
    "reset() should pass the expected actor attribution"
  );

  uninstallStub.restore();
});

add_task(function test_isBlocked_reflects_enabled_and_userEnabled() {
  clearSTGPrefs();

  Services.prefs.setBoolPref(PREF_ENABLED, true);
  Services.prefs.setBoolPref(PREF_USER_ENABLED, true);
  Assert.ok(
    !SmartTabGroupingManager.isBlocked,
    "isBlocked() should be false when enabled=true and userEnabled=true"
  );

  Services.prefs.setBoolPref(PREF_ENABLED, false);
  Services.prefs.setBoolPref(PREF_USER_ENABLED, true);
  Assert.ok(
    SmartTabGroupingManager.isBlocked,
    "isBlocked() should be true when enabled=false"
  );

  Services.prefs.setBoolPref(PREF_ENABLED, true);
  Services.prefs.setBoolPref(PREF_USER_ENABLED, false);
  Assert.ok(
    SmartTabGroupingManager.isBlocked,
    "isBlocked() should be true when userEnabled=false"
  );
});

add_task(async function test_disable_sets_prefs_false_and_uninstalls_models() {
  clearSTGPrefs();
  await SmartTabGroupingManager.enable();
  Assert.ok(
    SmartTabGroupingManager.isEnabled,
    "Sanity check: enabled before disable()"
  );

  const uninstallStub = sinon.stub(MLUninstallService, "uninstall").resolves();

  await SmartTabGroupingManager.block();

  Assert.equal(
    Services.prefs.getBoolPref(PREF_ENABLED, true),
    false,
    "disable() should set browser.tabs.groups.smart.enabled=false"
  );
  Assert.equal(
    Services.prefs.getBoolPref(PREF_USER_ENABLED, true),
    false,
    "disable() should set browser.tabs.groups.smart.userEnabled=false"
  );
  Assert.equal(
    Services.prefs.getBoolPref(PREF_OPTIN, true),
    false,
    "disable() should set browser.tabs.groups.smart.optin=false"
  );

  Assert.ok(
    !SmartTabGroupingManager.isEnabled,
    "After disable(), isEnabled should be false"
  );

  Assert.ok(
    uninstallStub.calledOnce,
    "disable() should uninstall ML engine files via MLUninstallService.uninstall()"
  );

  const expectedEngineIds = [
    SMART_TAB_GROUPING_CONFIG.topicGeneration.engineId,
    SMART_TAB_GROUPING_CONFIG.embedding.engineId,
  ].sort();

  const uninstallArgs = uninstallStub.getCall(0).args[0];
  Assert.deepEqual(
    (uninstallArgs.engineIds || []).slice().sort(),
    expectedEngineIds,
    "disable() should uninstall files for both STG engines"
  );
  Assert.equal(
    uninstallArgs.actor,
    "SmartTabGrouping",
    "disable() should pass the expected actor attribution"
  );

  uninstallStub.restore();
});
