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

"use strict";

const { XPCOMUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/XPCOMUtils.sys.mjs"
);

const { PrivacyMetricsService } = ChromeUtils.importESModule(
  "moz-src:///browser/components/protections/PrivacyMetricsService.sys.mjs"
);

const { Sqlite } = ChromeUtils.importESModule(
  "resource://gre/modules/Sqlite.sys.mjs"
);

XPCOMUtils.defineLazyServiceGetter(
  this,
  "TrackingDBService",
  "@mozilla.org/tracking-db-service;1",
  Ci.nsITrackingDBService
);

ChromeUtils.defineLazyGetter(this, "DB_PATH", function () {
  return PathUtils.join(PathUtils.profileDir, "protections.sqlite");
});

const SQL = {
  insertCustomTimeEvent:
    "INSERT INTO events (type, count, timestamp) " +
    "VALUES (:type, :count, date(:timestamp));",
};

// Setup profile and enable tracking database
do_get_profile();
Services.prefs.setBoolPref("browser.contentblocking.database.enabled", true);

add_setup(async function () {
  // Initialize the database schema
  await TrackingDBService.saveEvents(JSON.stringify({}));
  // Clear any existing data before tests
  await TrackingDBService.clearAll();
});

registerCleanupFunction(() => {
  Services.prefs.clearUserPref("browser.contentblocking.database.enabled");
});

add_task(async function test_getWeeklyStats_empty() {
  info("Test that getWeeklyStats returns empty stats for new users");

  const stats = await PrivacyMetricsService.getWeeklyStats();

  Assert.equal(stats.total, 0, "Total should be 0 for new users");
  Assert.equal(stats.trackers, 0, "Trackers should be 0");
  Assert.equal(stats.cookies, 0, "Cookies should be 0");
  Assert.equal(stats.fingerprinters, 0, "Fingerprinters should be 0");
  Assert.equal(stats.cryptominers, 0, "Cryptominers should be 0");
  Assert.equal(stats.socialTrackers, 0, "Social trackers should be 0");
  Assert.greater(stats.lastUpdated, 0, "Should have a timestamp");
});

add_task(async function test_getWeeklyStats_aggregation() {
  info("Test that getWeeklyStats correctly aggregates by category");

  const db = await Sqlite.openConnection({ path: DB_PATH });
  const now = new Date().toISOString();

  // Insert test data directly into database
  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.TRACKERS_ID,
    count: 10,
    timestamp: now,
  });
  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.TRACKING_COOKIES_ID,
    count: 5,
    timestamp: now,
  });
  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.FINGERPRINTERS_ID,
    count: 3,
    timestamp: now,
  });
  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.CRYPTOMINERS_ID,
    count: 2,
    timestamp: now,
  });
  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.SOCIAL_ID,
    count: 1,
    timestamp: now,
  });

  await db.close();

  const stats = await PrivacyMetricsService.getWeeklyStats();

  Assert.equal(stats.total, 21, "Total should be sum of all categories");
  Assert.equal(stats.trackers, 10, "Trackers counted correctly");
  Assert.equal(stats.cookies, 5, "Cookies counted correctly");
  Assert.equal(stats.fingerprinters, 3, "Fingerprinters counted correctly");
  Assert.equal(stats.cryptominers, 2, "Cryptominers counted correctly");
  Assert.equal(stats.socialTrackers, 1, "Social trackers counted correctly");

  // Cleanup
  await TrackingDBService.clearAll();
});

add_task(async function test_getWeeklyStats_multiple_days() {
  info("Test aggregation across multiple days");

  const db = await Sqlite.openConnection({ path: DB_PATH });
  const now = Date.now();
  const oneDayAgo = new Date(now - 24 * 60 * 60 * 1000).toISOString();
  const twoDaysAgo = new Date(now - 2 * 24 * 60 * 60 * 1000).toISOString();
  const today = new Date().toISOString();

  // Day 1 (two days ago)
  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.TRACKERS_ID,
    count: 5,
    timestamp: twoDaysAgo,
  });

  // Day 2 (one day ago)
  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.TRACKERS_ID,
    count: 7,
    timestamp: oneDayAgo,
  });

  // Day 3 (today)
  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.TRACKERS_ID,
    count: 8,
    timestamp: today,
  });

  await db.close();

  const stats = await PrivacyMetricsService.getWeeklyStats();

  Assert.equal(
    stats.trackers,
    20,
    "Should sum trackers across all days in the week"
  );
  Assert.equal(stats.total, 20, "Total should equal sum of all categories");

  await TrackingDBService.clearAll();
});

add_task(async function test_old_data_ignored() {
  info("Test that data older than 7 days is not included");

  const db = await Sqlite.openConnection({ path: DB_PATH });
  const now = Date.now();
  const eightDaysAgo = new Date(now - 8 * 24 * 60 * 60 * 1000).toISOString();
  const threeDaysAgo = new Date(now - 3 * 24 * 60 * 60 * 1000).toISOString();

  // Add old data (8 days ago)
  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.TRACKERS_ID,
    count: 100,
    timestamp: eightDaysAgo,
  });

  // Add recent data (3 days ago)
  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.TRACKERS_ID,
    count: 50,
    timestamp: threeDaysAgo,
  });

  await db.close();

  const stats = await PrivacyMetricsService.getWeeklyStats();

  Assert.equal(stats.trackers, 50, "Should only count data from last 7 days");

  await TrackingDBService.clearAll();
});

add_task(async function test_exactly_7_days_boundary() {
  info("Test 7-day boundary precisely - 6 days ago included, 8 days excluded");

  const db = await Sqlite.openConnection({ path: DB_PATH });
  const now = Date.now();
  // 6 days ago (should be included - within 7-day window)
  const sixDaysAgo = new Date(now - 6 * 24 * 60 * 60 * 1000).toISOString();
  // 8 days ago (should be excluded - outside 7-day window)
  const eightDaysAgo = new Date(now - 8 * 24 * 60 * 60 * 1000).toISOString();

  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.TRACKERS_ID,
    count: 25,
    timestamp: sixDaysAgo,
  });

  await db.execute(SQL.insertCustomTimeEvent, {
    type: TrackingDBService.TRACKERS_ID,
    count: 75,
    timestamp: eightDaysAgo,
  });

  await db.close();

  const stats = await PrivacyMetricsService.getWeeklyStats();

  Assert.equal(
    stats.trackers,
    25,
    "Should include 6-day-old data but exclude 8-day-old data"
  );

  await TrackingDBService.clearAll();
});
