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

"use strict";

ChromeUtils.defineESModuleGetters(this, {
  ContentRelevancyManager:
    "resource://gre/modules/ContentRelevancyManager.sys.mjs",
  Interest:
    "moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustRelevancy.sys.mjs",
  InterestVector:
    "moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustRelevancy.sys.mjs",
  sinon: "resource://testing-common/Sinon.sys.mjs",
});

const PREF_CONTENT_RELEVANCY_ENABLED = "toolkit.contentRelevancy.enabled";

let gSandbox;
let gFakeStore;
let gFakeRustRelevancyStore;

const TEST_INTEREST_VECTOR = new InterestVector({
  animals: 50,
  arts: 0,
  autos: 0,
  business: 0,
  career: 0,
  education: 50,
  fashion: 0,
  finance: 0,
  food: 0,
  government: 0,
  hobbies: 0,
  home: 0,
  news: 0,
  realEstate: 0,
  society: 0,
  sports: 0,
  tech: 0,
  travel: 0,
  inconclusive: 50,
});

const SINGLE_INTEREST_HIT = [Interest.ANIMALS];
const MULTI_INTEREST_HITS = [Interest.ANIMALS, Interest.EDUCATION];
const INTEREST_MISSES = [Interest.SPORTS, Interest.NEWS];
const INTEREST_INCONCLUSIVE = [Interest.INCONCLUSIVE];
// Original encoding for `Interest.EDUCATION`
const SINGLE_INTEREST_HIT_ORIGINAL_ENCODING = [6];
const INTEREST_INCONCLUSIVE_ORIGINAL_ENCODING = [0];
const INVALID_INTEREST = [-1];

add_setup(() => {
  gSandbox = sinon.createSandbox();
  Services.prefs.setBoolPref(PREF_CONTENT_RELEVANCY_ENABLED, true);
  gFakeStore = {
    close: gSandbox.fake(),
    userInterestVector: gSandbox.stub(),
  };
  gFakeRustRelevancyStore = {
    init: gSandbox.fake.returns(gFakeStore),
  };

  registerCleanupFunction(() => {
    Services.prefs.clearUserPref(PREF_CONTENT_RELEVANCY_ENABLED);
    ContentRelevancyManager.uninit();
    gSandbox.restore();
  });
});

add_task(async function test_getUserInterestVector() {
  gFakeStore.userInterestVector.resolves(TEST_INTEREST_VECTOR);

  ContentRelevancyManager.init(gFakeRustRelevancyStore);
  const vector = await ContentRelevancyManager.getUserInterestVector();

  Assert.deepEqual(
    TEST_INTEREST_VECTOR,
    vector,
    "Should return the vector from the store"
  );

  ContentRelevancyManager.uninit();
  gSandbox.restore();
});

add_task(async function test_getUserInterestVector_uninitialized() {
  const vector = await ContentRelevancyManager.getUserInterestVector();

  Assert.equal(
    null,
    vector,
    "Should return null when the manager is uninitialized"
  );
});

add_task(async function test_getUserInterestVector() {
  gFakeStore.userInterestVector.throws();

  ContentRelevancyManager.init(gFakeRustRelevancyStore);
  const vector = await ContentRelevancyManager.getUserInterestVector();

  Assert.equal(
    null,
    vector,
    "Should return null when it fails to fetch from the store."
  );

  ContentRelevancyManager.uninit();
  gSandbox.restore();
});

add_task(async function test_score() {
  gFakeStore.userInterestVector.resolves(TEST_INTEREST_VECTOR);

  ContentRelevancyManager.init(gFakeRustRelevancyStore);

  const scoreSingleHit =
    await ContentRelevancyManager.score(SINGLE_INTEREST_HIT);

  Assert.greater(
    scoreSingleHit,
    0,
    "Single interest hit should yield a positive score"
  );

  const scoreMultiHits =
    await ContentRelevancyManager.score(MULTI_INTEREST_HITS);

  Assert.greater(
    scoreMultiHits,
    scoreSingleHit,
    "Multiple interest hits should yield a higher relevance score"
  );

  const scoreMiss = await ContentRelevancyManager.score(INTEREST_MISSES);

  Assert.equal(0, scoreMiss, "Interest misses should yield relevance score 0");

  const scoreInconclusive = await ContentRelevancyManager.score(
    INTEREST_INCONCLUSIVE
  );

  Assert.equal(
    0,
    scoreInconclusive,
    "Interest INCONCLUSIVE should yield relevance score 0"
  );

  ContentRelevancyManager.uninit();
  gSandbox.restore();
});

add_task(async function test_score_uninitialized() {
  await Assert.rejects(
    ContentRelevancyManager.score(),
    /User interest vector not ready/,
    "Should throw if the manager is uninitialized"
  );
});

add_task(async function test_score_invalid_interests() {
  gFakeStore.userInterestVector.resolves(TEST_INTEREST_VECTOR);

  ContentRelevancyManager.init(gFakeRustRelevancyStore);

  await Assert.rejects(
    ContentRelevancyManager.score(INVALID_INTEREST),
    /Invalid interest value/,
    "Should throw for invalid interest"
  );

  ContentRelevancyManager.uninit();
  gSandbox.restore();
});

add_task(async function test_score_with_interest_adjustment() {
  gFakeStore.userInterestVector.resolves(TEST_INTEREST_VECTOR);

  ContentRelevancyManager.init(gFakeRustRelevancyStore);

  const score = await ContentRelevancyManager.score(
    SINGLE_INTEREST_HIT_ORIGINAL_ENCODING
  );

  Assert.greater(
    score,
    0,
    "Single interest hit w/ original encoding should yield a positive score"
  );

  const scoreInconclusive = await ContentRelevancyManager.score(
    INTEREST_INCONCLUSIVE_ORIGINAL_ENCODING
  );

  Assert.equal(
    0,
    scoreInconclusive,
    "Interest INCONCLUSIVE w/ original encoding should yield relevance score 0"
  );

  ContentRelevancyManager.uninit();
  gSandbox.restore();
});
