import assert from "node:assert/strict";
import test from "node:test";
import {
  getCosmeticCssForHostname,
  parseFilterRules,
  renderPage,
  shouldBlockRequestWithRules
} from "../src/archiver.mjs";

test("parses EasyList-style network rules, exceptions, and badfilter entries", () => {
  const rules = parseFilterRules(`
    [Adblock Plus 2.0]
    ||ads.example.com^$script,third-party
    @@||ads.example.com/allowed.js$script,domain=publisher.test
    banner$~image
    ||disabled.example^$script
    ||disabled.example^$script,badfilter
  `);

  assert.equal(
    shouldBlockRequestWithRules(
      rules,
      "https://ads.example.com/banner.js",
      "script",
      "www.publisher.test"
    ),
    true
  );
  assert.equal(
    shouldBlockRequestWithRules(
      rules,
      "https://ads.example.com/banner.png",
      "image",
      "www.publisher.test"
    ),
    false
  );
  assert.equal(
    shouldBlockRequestWithRules(
      rules,
      "https://ads.example.com/allowed.js",
      "script",
      "www.publisher.test"
    ),
    false
  );
  assert.equal(
    shouldBlockRequestWithRules(
      rules,
      "https://disabled.example/ad.js",
      "script",
      "www.publisher.test"
    ),
    false
  );
});

test("applies cosmetic filters with domain exceptions and skips unsupported procedural selectors", () => {
  const rules = parseFilterRules(`
    ##.generic-ad
    example.com##.site-ad
    example.com#@#.generic-ad
    ~news.example.com,example.com##.except-news
    example.*##.entity-ad
    bad.example##div:has-text(ad)
    foo.com#$#.adguard { display: none !important; }
  `);

  const exampleCss = getCosmeticCssForHostname(rules, "www.example.com").join("\n");
  assert.doesNotMatch(exampleCss, /\.generic-ad/);
  assert.match(exampleCss, /\.site-ad/);
  assert.match(exampleCss, /\.except-news/);
  assert.match(exampleCss, /\.entity-ad/);

  const newsCss = getCosmeticCssForHostname(rules, "news.example.com").join("\n");
  assert.doesNotMatch(newsCss, /\.except-news/);
  assert.equal(getCosmeticCssForHostname(rules, "bad.example").length, 1);
  assert.match(getCosmeticCssForHostname(rules, "foo.com").join("\n"), /\.adguard/);
});

test("renderPage removes common cookie consent overlays before snapshot", async () => {
  const html = `<!doctype html>
    <html class="js sp-message-open" style="overflow: hidden;">
      <body class="didomi-popup-open" style="overflow: hidden;">
        <main>Article text</main>
        <div id="sp_message_container_123" role="dialog" aria-modal="true">
          <iframe id="sp_message_iframe_123" title="SP Consent Message" srcdoc="<p>Cookies on FT Sites</p>"></iframe>
        </div>
      </body>
    </html>`;

  const rendered = await renderPage(`data:text/html,${encodeURIComponent(html)}`, {
    userscriptDelay: 0
  });

  assert.match(rendered, /Article text/);
  assert.doesNotMatch(rendered, /sp_message_container_123/);
  assert.doesNotMatch(rendered, /sp_message_iframe_123/);
  assert.doesNotMatch(rendered, /<html[^>]*class="[^"]*sp-message-open/i);
  assert.doesNotMatch(rendered, /<body[^>]*class="[^"]*didomi-popup-open/i);
});

test("renderPage removes Admiral adblock walls before snapshot", async () => {
  const html = `<!doctype html>
    <html style="overflow: hidden;">
      <body style="overflow: hidden;">
        <main>Article text</main>
        <div style="position: fixed; inset: 0; z-index: 2147483647; overflow: auto;">
          <section>
            <h2>Support The Verge by allowing ads</h2>
            <p>Or <a href="https://www.theverge.com/subscribe?itm_campaign=admiraladblock&itm_source=popup">subscribe</a> to continue using your ad blocker uninterrupted.</p>
            <button>ALLOW ADS</button>
            <a href="https://%67e%74%61%64mi%72%61l.com/pb/">Powered By</a>
          </section>
        </div>
      </body>
    </html>`;

  const rendered = await renderPage(`data:text/html,${encodeURIComponent(html)}`, {
    userscriptDelay: 0
  });

  assert.match(rendered, /Article text/);
  assert.doesNotMatch(rendered, /Support The Verge by allowing ads/);
  assert.doesNotMatch(rendered, /ALLOW ADS/);
  assert.doesNotMatch(rendered, /getadmiral|%67e%74%61%64mi%72%61l/i);
  assert.doesNotMatch(rendered, /<html[^>]*style="[^"]*overflow:\s*hidden/i);
  assert.doesNotMatch(rendered, /<body[^>]*style="[^"]*overflow:\s*hidden/i);
});

test("renderPage serializes CSSOM-inserted style rules", async () => {
  const html = `<!doctype html>
    <html>
      <head>
        <style id="runtime-style"></style>
        <script>
          document
            .getElementById("runtime-style")
            .sheet
            .insertRule(".runtime-rule { color: rgb(1, 2, 3); }", 0);
        </script>
      </head>
      <body><div class="runtime-rule">Styled by CSSOM</div></body>
    </html>`;

  const rendered = await renderPage(`data:text/html,${encodeURIComponent(html)}`, {
    userscriptDelay: 0
  });

  assert.match(rendered, /<style id="runtime-style">[\s\S]*\.runtime-rule/);
  assert.match(rendered, /color:\s*rgb\(1,\s*2,\s*3\)/);
});
