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

"use strict";

// Load anti-tracking test utilities
Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/toolkit/components/antitracking/test/browser/head.js",
  this
);

const BLOCKED_STATE = "Blocked 🛑";
const NOT_BLOCKED_STATE = "Not Blocked";

/**
 * Creates and loads a tracker iframe in the current page.
 * Similar to the approach used in browser_siteSpecificWorkAroundsComplex.js
 */
async function loadTracker(
  browser,
  trackerUrl = "https://tracking.example.org"
) {
  const blockedPromise = waitForContentBlockingEvent(window).then(
    () => "blocked"
  );

  const loadPromise = SpecialPowers.spawn(
    browser,
    [trackerUrl],
    async function (url) {
      // Create a unique iframe ID to avoid conflicts
      const iframe = content.document.createElement("iframe");

      const iframeLoadPromise = ContentTaskUtils.waitForEvent(
        iframe,
        "load"
      ).then(() => "loaded");

      iframe.src = url;
      content.document.body.appendChild(iframe);
      return iframeLoadPromise;
    }
  );

  return Promise.race([loadPromise, blockedPromise]);
}

add_setup(async function () {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["browser.contentblocking.category", "strict"],
      ["privacy.trackingprotection.enabled", true],
      ["privacy.trackingprotection.allow_list.baseline.enabled", false],
      ["privacy.trackingprotection.allow_list.convenience.enabled", false],
      [
        "urlclassifier.trackingTable.testEntries",
        "tracking.example.org,social-tracking.example.org",
      ],
      [
        "urlclassifier.trackingAnnotationTable.testEntries",
        "tracking.example.org,social-tracking.example.org",
      ],
    ],
  });
  await UrlClassifierTestUtils.addTestTrackers();
  registerCleanupFunction(async () => {
    UrlClassifierTestUtils.cleanupTestTrackers();
  });
});

async function cleanup(toolbox) {
  await closeToolboxAndTab(toolbox);
}

/**
 * Test that a single blocked request is shown in the panel
 */
add_task(async function test_single_blocked_request_shown() {
  const tab = await addTab("https://example.com/");
  const toolbox = await openToolboxForTab(tab, "antitracking");
  const panel = toolbox.getCurrentPanel();
  const panelWindow = panel.panelWin;

  const webcompatDebugger = panelWindow.AntiTracking.debugger;

  const result = await loadTracker(tab.linkedBrowser);
  is(result, "blocked", "Tracker should be blocked");

  const hasTrackers = !!Object.keys(webcompatDebugger.allTrackers).length;

  ok(hasTrackers, "There should be at least one tracker in the debugger");

  const trackerRows = panelWindow.document.querySelectorAll(
    "#tracker-table tbody tr"
  );
  Assert.equal(
    trackerRows.length,
    1,
    "There should be one tracker shown in the panel"
  );

  // Check that the blocked tracker is displayed
  const blockedColumn = trackerRows[0].querySelector("td:nth-child(2)");
  is(
    blockedColumn.textContent,
    BLOCKED_STATE,
    "The tracker should be marked as blocked"
  );

  // Check hostname is displayed
  const hostnameColumn = trackerRows[0].querySelector("td:nth-child(3)");
  ok(
    hostnameColumn.textContent.includes("tracking.example.org"),
    "Tracker hostname should be displayed"
  );

  await cleanup(toolbox);
});

/**
 * Test that when no requests are blocked, nothing is shown in the panel
 */
add_task(async function test_no_blocked_requests_shown() {
  const tab = await addTab("https://example.com/");
  const toolbox = await openToolboxForTab(tab, "antitracking");
  const panel = toolbox.getCurrentPanel();
  const panelWindow = panel.panelWin;

  const table = panelWindow.document.getElementById("tracker-table");
  const trackerRows = table.querySelectorAll("tbody tr");

  is(
    trackerRows.length,
    0,
    "No trackers should be shown when none are blocked"
  );

  // Should show the "no content" message
  const noContentMessage = panelWindow.document.querySelector(
    ".no-content-message"
  );
  ok(
    noContentMessage &&
      noContentMessage.textContent.includes("No blocked resources"),
    "Should show no blocked resources message"
  );

  await cleanup(toolbox);
});

/**
 * Test that unblocking a tracker works correctly
 */
add_task(async function test_unblock_single_request() {
  const tab = await addTab("https://example.com/");
  const toolbox = await openToolboxForTab(tab, "antitracking");
  const panel = toolbox.getCurrentPanel();
  const panelWindow = panel.panelWin;
  const webcompatDebugger = panelWindow.AntiTracking.debugger;

  let result = await loadTracker(tab.linkedBrowser);
  is(result, "blocked", "Tracker should be blocked initially");

  const trackerRows = panelWindow.document.querySelectorAll(
    "#tracker-table tbody tr"
  );
  Assert.equal(
    trackerRows.length,
    1,
    "There should be one tracker shown in the panel"
  );

  const firstRow = trackerRows[0];

  const blockedColumn = firstRow.querySelector("td:nth-child(2)");
  is(
    blockedColumn.textContent,
    BLOCKED_STATE,
    "Tracker should initially be blocked"
  );

  const unblockButton = firstRow.querySelector("td:last-child button");
  is(unblockButton.textContent, "Unblock", "Button should say 'Unblock'");

  const browserLoadedPromise = BrowserTestUtils.browserLoaded(
    tab.linkedBrowser
  );

  unblockButton.click();
  await browserLoadedPromise;

  const updatedTrackerRows = panelWindow.document.querySelectorAll(
    "#tracker-table tbody tr"
  );
  const updatedFirstRow = updatedTrackerRows[0];

  const updatedBlockedColumn = updatedFirstRow.querySelector("td:nth-child(2)");
  is(
    updatedBlockedColumn.textContent,
    NOT_BLOCKED_STATE,
    "Tracker should now be unblocked in UI"
  );

  const updatedButton = updatedFirstRow.querySelector("td:last-child button");
  is(updatedButton.textContent, "Block", "Button should now say 'Block'");

  ok(
    webcompatDebugger.unblockedChannels.has("tracking.example.org"),
    "Tracker should now be unblocked in debugger state"
  );

  await reloadSelectedTab();

  // Test that the tracker would now load
  result = await loadTracker(tab.linkedBrowser);
  is(result, "loaded", "Tracker should load successfully after unblocking");

  await cleanup(toolbox);
});

/**
 * Test that clicking the "block" button for a request blocks it
 */
add_task(async function test_block_unblocked_channel() {
  const tab = await addTab("https://example.com/");
  const toolbox = await openToolboxForTab(tab, "antitracking");
  const panel = toolbox.getCurrentPanel();
  const panelWindow = panel.panelWin;
  const webcompatDebugger = panelWindow.AntiTracking.debugger;

  // Set up the debugger state to simulate an unblocked tracker
  webcompatDebugger.unblockedChannels = new Set(["tracking.example.org"]);
  webcompatDebugger.allTrackers = {
    "tracking.example.org": "Tracking Protection",
  };

  webcompatDebugger.populateTrackerTable();

  const firstRow = panelWindow.document.querySelectorAll(
    "#tracker-table tbody tr"
  )[0];

  const blockButton = firstRow.querySelector("td:last-child button");
  ok(blockButton, "Block button should be present");
  is(blockButton.textContent, "Block", "Button should say 'Block'");

  const browserLoadedPromise = BrowserTestUtils.browserLoaded(
    tab.linkedBrowser
  );
  blockButton.click();
  await browserLoadedPromise;

  const updatedTrackerRows = panelWindow.document.querySelectorAll(
    "#tracker-table tbody tr"
  );
  const updatedFirstRow = updatedTrackerRows[0];

  const updatedBlockedColumn = updatedFirstRow.querySelector("td:nth-child(2)");
  is(
    updatedBlockedColumn.textContent,
    BLOCKED_STATE,
    "Tracker should now be blocked in UI"
  );

  const updatedButton = updatedFirstRow.querySelector("td:last-child button");
  is(updatedButton.textContent, "Unblock", "Button should now say 'Unblock'");

  ok(
    !webcompatDebugger.unblockedChannels.has("tracking.example.org"),
    "Tracker should now be blocked (removed from unblockedChannels)"
  );

  const result = await loadTracker(tab.linkedBrowser);
  is(
    result,
    "blocked",
    "Tracker should be blocked after clicking block button"
  );

  await cleanup(toolbox);
});

/**
 * Test when there are multiple trackers and one is unblocked, the others are still blocked
 */
add_task(async function test_multiple_trackers_one_unblocked() {
  const tab = await addTab("https://example.com/");
  const toolbox = await openToolboxForTab(tab, "antitracking");
  const panel = toolbox.getCurrentPanel();
  const panelWindow = panel.panelWin;

  // Load multiple trackers
  let result = await loadTracker(
    tab.linkedBrowser,
    "https://tracking.example.org"
  );
  is(result, "blocked", "First tracker should be blocked");

  result = await loadTracker(
    tab.linkedBrowser,
    "https://social-tracking.example.org"
  );
  is(result, "blocked", "Second tracker should also be blocked");

  const trackerRows = panelWindow.document.querySelectorAll(
    "#tracker-table tbody tr"
  );
  Assert.equal(
    trackerRows.length,
    2,
    "Two trackers should be shown in the panel"
  );

  const firstRow = trackerRows[0];
  const unblockButton = firstRow.querySelector("td:last-child button");

  const browserLoadedPromise = BrowserTestUtils.browserLoaded(
    tab.linkedBrowser
  );
  unblockButton.click();

  await browserLoadedPromise;

  const updatedTrackerRows = panelWindow.document.querySelectorAll(
    "#tracker-table tbody tr"
  );
  const updatedFirstRow = updatedTrackerRows[0];

  const updatedBlockedColumn = updatedFirstRow.querySelector("td:nth-child(2)");
  is(
    updatedBlockedColumn.textContent,
    NOT_BLOCKED_STATE,
    "First tracker should now be unblocked in UI"
  );

  result = await loadTracker(tab.linkedBrowser, "https://tracking.example.org");
  is(result, "loaded", "First tracker should be loaded");

  await cleanup(toolbox);
});

/**
 * Test the "unblock selected" button functionality with multiple selected trackers
 */
add_task(async function test_unblock_selected_button() {
  const tab = await addTab("https://example.com/");
  const toolbox = await openToolboxForTab(tab, "antitracking");
  const panel = toolbox.getCurrentPanel();
  const panelWindow = panel.panelWin;
  const webcompatDebugger = panelWindow.AntiTracking.debugger;

  // Load two trackers
  let result = await loadTracker(
    tab.linkedBrowser,
    "https://tracking.example.org"
  );
  is(result, "blocked", "First tracker should be blocked");

  result = await loadTracker(
    tab.linkedBrowser,
    "https://social-tracking.example.org"
  );
  is(result, "blocked", "Second tracker should also be blocked");

  const rowCheckboxes = panelWindow.document.querySelectorAll(
    "#tracker-table tbody .row-checkbox"
  );
  is(rowCheckboxes.length, 2, "Two row checkboxes should be present");

  rowCheckboxes[0].click();
  rowCheckboxes[1].click();

  const unblockSelectedButton =
    panelWindow.document.getElementById("unblock-selected");
  ok(unblockSelectedButton, "Unblock selected button should be present");

  const browserLoadedPromise = BrowserTestUtils.browserLoaded(
    tab.linkedBrowser
  );

  unblockSelectedButton.click();

  await browserLoadedPromise;

  // Verify both trackers are now unblocked in the debugger state
  ok(
    webcompatDebugger.unblockedChannels.has("tracking.example.org"),
    "First tracker should be unblocked in debugger state"
  );
  ok(
    webcompatDebugger.unblockedChannels.has("social-tracking.example.org"),
    "Second tracker should be unblocked in debugger state"
  );

  // Verify UI shows both trackers as unblocked
  const updatedTrackerRows = panelWindow.document.querySelectorAll(
    "#tracker-table tbody tr"
  );
  // Verify buttons changed to "Block"
  const firstRowButton = updatedTrackerRows[0].querySelector(
    "td:last-child button"
  );
  const secondRowButton = updatedTrackerRows[1].querySelector(
    "td:last-child button"
  );

  is(
    firstRowButton.textContent,
    "Block",
    "First tracker button should say 'Block'"
  );
  is(
    secondRowButton.textContent,
    "Block",
    "Second tracker button should say 'Block'"
  );

  // Test that both trackers now load successfully
  await reloadSelectedTab();

  result = await loadTracker(tab.linkedBrowser, "https://tracking.example.org");
  is(result, "loaded", "First tracker should load after bulk unblock");

  result = await loadTracker(
    tab.linkedBrowser,
    "https://social-tracking.example.org"
  );
  is(result, "loaded", "Second tracker should load after bulk unblock");

  await cleanup(toolbox);
});
