// AdBlockMaxxer — YouTube core ad stripper (runs in the page's MAIN world at document_start).
//
// Strips ad placements out of the player response before the player sees them. This is the
// simple, safe path that gives ad-free YouTube on normal (non-enforced) accounts.
//
// SERVER-SIDE ENFORCEMENT SAFEGUARD (2026): on accounts YouTube has escalated to server-side
// ad enforcement, googlevideo.com/videoplayback returns 403 (no PoToken) whenever we strip the
// ad — the media bytes never arrive, so the video goes black. No client-side response edit can
// fix a server-side 403, and a PoToken can only be minted by WASM/BotGuard that Manifest V3
// cannot run. So instead of fighting an unwinnable fight (and leaving the user with a black
// screen that looks like WE broke it), we DETECT the 403, AUTO-PAUSE YouTube stripping for a
// day (the video then plays — with ads — instead of failing), and SILENTLY RE-TEST: the moment
// YouTube rotates the account back out of the enforcement bucket, a strip succeeds and we go
// ad-free again automatically. State lives in localStorage (synchronous + shared with the
// ISOLATED world + persists across reloads, per-origin = per-account-ish).
// See project_adblockmaxxer memory for the full v1.2.16–1.2.21 investigation.

(function () {
  "use strict";
  if (window.__adblockmaxxerYT) return;
  window.__adblockmaxxerYT = true;

  var ENF_KEY = "__abm_yt_enforced";        // localStorage: ms timestamp to auto-pause until
  var RELOAD_KEY = "__abm_yt_reloaded";     // sessionStorage: reload-loop guard
  var PAUSE_MS = 24 * 60 * 60 * 1000;       // re-test the account once a day

  function enforcedUntil() {
    try { return parseInt(localStorage.getItem(ENF_KEY) || "0", 10) || 0; } catch (e) { return 0; }
  }
  // Read once at document_start. When paused we don't strip, so the server is satisfied (an ad
  // plays) and the video loads normally instead of 403'ing to a black screen.
  var paused = enforcedUntil() > Date.now();
  if (paused) { try { sessionStorage.removeItem(RELOAD_KEY); } catch (e) {} } // re-arm next re-test

  function post(obj) { try { window.postMessage(obj, "*"); } catch (e) {} }

  var lastReport = 0;
  function report() {
    var now = +new Date();
    if (now - lastReport < 800) return;
    lastReport = now;
    post({ __abmYT: "ad" });
  }

  var strippedAd = false; // did we actually gut an ad-bearing response this page load?
  function strip(data) {
    if (paused) return data; // enforced: leave ads in so the server serves the video bytes
    if (!data || typeof data !== "object") return data;
    try {
      var hadAd = false;
      if (Array.isArray(data.adPlacements) && data.adPlacements.length) hadAd = true;
      if (Array.isArray(data.adSlots) && data.adSlots.length) hadAd = true;
      if (data.playerAds) hadAd = true;
      if ("adPlacements" in data) data.adPlacements = [];
      if ("adSlots" in data) data.adSlots = [];
      if ("playerAds" in data) data.playerAds = false;
      if ("adParams" in data) delete data.adParams;
      if (hadAd) { strippedAd = true; report(); }
      if (data.playerResponse) strip(data.playerResponse);
      if (data.response && data.response.playerResponse) strip(data.response.playerResponse);
    } catch (e) { /* never break the page */ }
    return data;
  }

  // ---- enforcement detection ------------------------------------------------
  // A 403 on a googlevideo videoplayback request WHILE WE ARE STRIPPING = YouTube refusing
  // media without an ad (server-side enforcement) -> auto-pause + reload so the video plays.
  // A 2xx AFTER we stripped a real ad = enforcement is gone -> tell the popup we recovered.
  var signalled = false;
  function isPlayback(url) {
    return typeof url === "string" &&
      url.indexOf("videoplayback") !== -1 &&
      url.indexOf("googlevideo.com") !== -1;
  }
  function onMediaStatus(url, status) {
    if (paused || signalled || !status || !isPlayback(url)) return;
    if (status === 403) {
      signalled = true;
      try { localStorage.setItem(ENF_KEY, String(Date.now() + PAUSE_MS)); } catch (e) {}
      post({ __abmYTEnforced: true });
      // Reload once so the page comes back up paused (and the video actually plays). The guard
      // stops any loop; once we're paused we won't strip, so we won't 403 again.
      try {
        if (!sessionStorage.getItem(RELOAD_KEY)) {
          sessionStorage.setItem(RELOAD_KEY, "1");
          location.reload();
        }
      } catch (e) {}
    } else if (status >= 200 && status < 300 && strippedAd) {
      signalled = true; // stripped an ad AND media still served => not enforced (anymore)
      post({ __abmYTOk: true });
    }
  }

  // 0) Watch fetch + XHR statuses for videoplayback (we only read .status — never touch bodies).
  try {
    var origFetch = window.fetch;
    window.fetch = function (input) {
      var url = typeof input === "string" ? input : (input && input.url) || "";
      return origFetch.apply(this, arguments).then(function (res) {
        try { onMediaStatus(url || (res && res.url) || "", res && res.status); } catch (e) {}
        return res;
      });
    };
  } catch (e) {}
  try {
    var origOpen = XMLHttpRequest.prototype.open;
    var origSend = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype.open = function (m, url) {
      try { this.__abmUrl = url; } catch (e) {}
      return origOpen.apply(this, arguments);
    };
    XMLHttpRequest.prototype.send = function () {
      var xhr = this;
      try {
        xhr.addEventListener("loadend", function () {
          try { onMediaStatus(xhr.__abmUrl || xhr.responseURL || "", xhr.status); } catch (e) {}
        });
      } catch (e) {}
      return origSend.apply(this, arguments);
    };
  } catch (e) {}

  // 1) JSON.parse — most embedded JSON + SPA player responses flow through here.
  try {
    var origParse = JSON.parse;
    JSON.parse = function () { return strip(origParse.apply(this, arguments)); };
  } catch (e) {}

  // 2) fetch/XHR responses for /youtubei/v1/player as you browse.
  try {
    var origJson = Response.prototype.json;
    Response.prototype.json = function () { return origJson.apply(this, arguments).then(strip); };
  } catch (e) {}

  // 3) The first video on a fresh load is inlined as window.ytInitialPlayerResponse (an object
  //    literal that never hits JSON.parse/fetch). Trap it and strip in place — store the stripped
  //    value and hand it back (no forced refetch, so nothing breaks on normal accounts).
  try {
    var storedPR;
    Object.defineProperty(window, "ytInitialPlayerResponse", {
      configurable: true,
      enumerable: true,
      get: function () { return storedPR; },
      set: function (v) { storedPR = (v && typeof v === "object") ? strip(v) : v; },
    });
  } catch (e) {}
})();
