const { RemoteSettingsClient } = ChromeUtils.importESModule(
  "resource://services-settings/RemoteSettingsClient.sys.mjs"
);
const { RemoteSettingsWorker } = ChromeUtils.importESModule(
  "resource://services-settings/RemoteSettingsWorker.sys.mjs"
);
const { SharedUtils } = ChromeUtils.importESModule(
  "resource://services-settings/SharedUtils.sys.mjs"
);

// A collection with a dump that's packaged on all builds where this test runs,
// including on Android at mobile/android/installer/package-manifest.in
const TEST_BUCKET = "main";
const TEST_COLLECTION = "password-recipes";

let client;
let DUMP_RECORDS;
let DUMP_LAST_MODIFIED;

add_task(async function setup() {
  // "services.settings.server" pref is not set.
  // Test defaults to an unreachable server,
  // and will only load from the dump if any.

  client = new RemoteSettingsClient(TEST_COLLECTION, {
    bucketName: TEST_BUCKET,
  });

  const dump = await SharedUtils.loadJSONDump(TEST_BUCKET, TEST_COLLECTION);
  DUMP_RECORDS = dump.data;
  DUMP_LAST_MODIFIED = dump.timestamp;

  // Dumps are fetched via the following, which sorts the records, newest first.
  // https://searchfox.org/mozilla-central/rev/5b3444ad300e244b5af4214212e22bd9e4b7088a/taskcluster/docker/periodic-updates/scripts/periodic_file_updates.sh#304
  equal(
    DUMP_LAST_MODIFIED,
    DUMP_RECORDS[0].last_modified,
    "records in dump ought to be sorted by last_modified"
  );
});

async function importData(records) {
  await RemoteSettingsWorker._execute("_test_only_import", [
    TEST_BUCKET,
    TEST_COLLECTION,
    records,
    records[0]?.last_modified || 0,
  ]);
}

async function clear_state() {
  await client.db.clear();
}

add_task(async function test_load_from_dump_when_offline() {
  // Baseline: verify that the collection is empty at first,
  // but non-empty after loading from the dump.
  const before = await client.get({ syncIfEmpty: false });
  equal(before.length, 0, "collection empty when offline");

  // should import from dump since collection was not initialized.
  const after = await client.get();
  equal(after.length, DUMP_RECORDS.length, "collection loaded from dump");
  equal(await client.getLastModified(), DUMP_LAST_MODIFIED, "dump's timestamp");
});
add_task(clear_state);

add_task(async function test_optional_skip_dump_after_empty_import() {
  // clear_state should have wiped the database.
  const before = await client.get({ syncIfEmpty: false });
  equal(before.length, 0, "collection empty after clearing");

  // Verify that the dump is not imported again by client.get()
  // when the database is initialized with an empty dump
  // with `loadDumpIfNewer` disabled.
  await importData([]); // <-- Empty set of records.

  const after = await client.get({ loadDumpIfNewer: false });
  equal(after.length, 0, "collection still empty due to import");
  equal(await client.getLastModified(), 0, "Empty dump has no timestamp");
});
add_task(clear_state);

add_task(async function test_optional_skip_dump_after_non_empty_import() {
  await importData([{ last_modified: 1234, id: "dummy" }]);

  const after = await client.get({ loadDumpIfNewer: false });
  equal(after.length, 1, "Imported dummy data");
  equal(await client.getLastModified(), 1234, "Expected timestamp of import");

  await importData([]);
  const after2 = await client.get({ loadDumpIfNewer: false });
  equal(after2.length, 0, "Previous data wiped on duplicate import");
  equal(await client.getLastModified(), 0, "Timestamp of empty collection");
});
add_task(clear_state);

add_task(async function test_load_dump_after_empty_import() {
  await importData([]); // <-- Empty set of records, i.e. last_modified = 0.

  const after = await client.get();
  equal(after.length, DUMP_RECORDS.length, "Imported dump");
  equal(await client.getLastModified(), DUMP_LAST_MODIFIED, "dump's timestamp");
});
add_task(clear_state);

add_task(async function test_load_dump_after_non_empty_import() {
  // Dump is updated regularly, verify that the dump matches our expectations
  // before running the test.
  ok(DUMP_LAST_MODIFIED > 1234, "Assuming dump to be newer than dummy 1234");

  await importData([{ last_modified: 1234, id: "dummy" }]);

  const after = await client.get();
  equal(after.length, DUMP_RECORDS.length, "Imported dump");
  equal(await client.getLastModified(), DUMP_LAST_MODIFIED, "dump's timestamp");
});
add_task(clear_state);

add_task(async function test_load_dump_after_import_from_broken_distro() {
  // Dump is updated regularly, verify that the dump matches our expectations
  // before running the test.
  ok(DUMP_LAST_MODIFIED > 1234, "Assuming dump to be newer than dummy 1234");

  // No last_modified time.
  await importData([{ id: "dummy" }]);

  const after = await client.get();
  equal(after.length, DUMP_RECORDS.length, "Imported dump");
  equal(await client.getLastModified(), DUMP_LAST_MODIFIED, "dump's timestamp");
});
add_task(clear_state);

add_task(async function test_skip_dump_if_same_last_modified() {
  await importData([{ last_modified: DUMP_LAST_MODIFIED, id: "dummy" }]);

  const after = await client.get();
  equal(after.length, 1, "Not importing dump when time matches");
  equal(await client.getLastModified(), DUMP_LAST_MODIFIED, "Same timestamp");
});
add_task(clear_state);
