// adeater-sweepers.js — DOM sweeping functions (eat* family) and eatAll orchestrator.
// Depends on: adeater-selectors.js (EAT_*, ENABLE_BAIT, AD_SELECTORS, etc.)
//             adeater-utils.js (stats, safeRemove, limitQuery, isProtected, etc.)

// ---------------------------------------------------------------------------
// 1. Ad eater
// ---------------------------------------------------------------------------
function eatAds() {
  if (!EAT_ADS) return;
  for (const sel of AD_SELECTORS) {
    try {
      const elements = document.querySelectorAll(sel);
      for (const el of elements) {
        if (safeRemove(el, "ad: " + sel)) {
          stats.adsRemoved++;
        }
      }
    } catch (e) {
      // Invalid selector — skip.
    }
  }
  try {
    const all = limitQuery("div, section, aside");
    for (const el of all) {
      if (isViewportOverlay(el) && safeRemove(el, "viewport-overlay ad")) {
        stats.adsRemoved++;
      }
    }
  } catch (e) {}
}

// ---------------------------------------------------------------------------
// 2. Tracker eater
// ---------------------------------------------------------------------------
function eatTrackers() {
  if (!EAT_TRACKERS) return;
  for (const sel of TRACKER_SELECTORS) {
    try {
      const elements = document.querySelectorAll(sel);
      for (const el of elements) {
        if (safeRemove(el, "tracker: " + sel)) {
          stats.trackersRemoved++;
        }
      }
    } catch (e) {}
  }
}

// ---------------------------------------------------------------------------
// 3. Autoplay eater
// ---------------------------------------------------------------------------
function eatAutoplay() {
  if (!EAT_AUTOPLAY) return;
  try {
    const videos = document.querySelectorAll("video");
    for (const v of videos) {
      const rect = v.getBoundingClientRect();
      if (rect.width < 100 && rect.height < 100) {
        if (safeRemove(v, "small autoplay video (" + rect.width + "x" + rect.height + ")")) {
          stats.videosPaused++;
        }
        continue;
      }
      if (v.autoplay && !v.paused) {
        try {
          v.pause();
          stats.videosPaused++;
        } catch (e) {}
      }
      const vh = window.innerHeight;
      const vw = window.innerWidth;
      const offscreen = rect.bottom < -100 || rect.top > vh + 100 ||
                        rect.right < -100 || rect.left > vw + 100;
      if (offscreen && !v.paused) {
        try {
          v.pause();
          stats.videosPaused++;
        } catch (e) {}
      }
      if (v.muted && v.autoplay && isHidden(v)) {
        if (safeRemove(v, "hidden muted autoplay video")) {
          stats.videosPaused++;
        }
      }
    }
  } catch (e) {}
}

// ---------------------------------------------------------------------------
// 4. Heavy DOM eater
// ---------------------------------------------------------------------------
function eatHeavy(candidates) {
  if (!EAT_HEAVY) return;
  try {
    const els = candidates || limitQuery("div, section, aside");
    for (const el of els) {
      if (!isHidden(el)) continue;
      if (visibleTextLength(el) > 0) continue;
      const childCount = el.children.length;
      if (childCount > 50) {
        if (safeRemove(el, "heavy hidden element (" + childCount + " children)")) {
          stats.heavyElementsRemoved++;
        }
        continue;
      }
      try {
        const bytes = estimateBytes(el);
        if (bytes > 102400 && el.querySelectorAll("*").length > 20) {
          if (safeRemove(el, "heavy hidden element (" + bytes + " bytes)")) {
            stats.heavyElementsRemoved++;
          }
        }
      } catch {}
    }
  } catch (e) {}
}

// ---------------------------------------------------------------------------
// 5. Empty placeholder eater
// ---------------------------------------------------------------------------
function eatEmptyPlaceholders(candidates) {
  if (!EAT_EMPTY) return;
  try {
    const els = candidates || limitQuery("div, section, aside");
    for (const el of els) {
      if (el.children.length > 3) continue;
      if (visibleTextLength(el) > 0) continue;
      const rect = el.getBoundingClientRect();
      if (rect.width < 50 || rect.height < 50) continue;
      const imgs = el.querySelectorAll("img");
      const iframes = el.querySelectorAll("iframe");
      if (imgs.length === 0 && iframes.length === 0) {
        if (safeRemove(el, "empty placeholder")) {
          stats.heavyElementsRemoved++;
        }
      }
    }
  } catch (e) {}
}

// ---------------------------------------------------------------------------
// 6. Text content ad detector
// ---------------------------------------------------------------------------
function eatByTextContent() {
  if (!EAT_TEXT_ADS) return;
  try {
    const candidates = limitQuery("div, section, aside, span, article, figure");
    for (const el of candidates) {
      if (isProtected(el)) continue;
      const text = (el.textContent || "").trim().toLowerCase();
      if (text.length > 300 || text.length < 5) continue;
      const tag = el.tagName;
      if (tag === "ARTICLE" || tag === "FIGURE") {
        const r = el.getBoundingClientRect();
        if (r.width > 600 || r.height > 500) continue;
      }
      const matched = AD_TEXT_KEYWORDS.some(kw => text.includes(kw));
      if (!matched) continue;
      const rect = el.getBoundingClientRect();
      if (rect.width > 800 || rect.height > 600) continue;
      if (safeRemove(el, "ad-text: matched ad keyword in text")) {
        stats.adsRemoved++;
      }
    }
  } catch (e) {}
}

// ---------------------------------------------------------------------------
// 7. Link destination ad detector
// ---------------------------------------------------------------------------
function eatByLinkDestination(divLike) {
  if (!EAT_LINKS) return;
  try {
    const iframes = document.querySelectorAll("iframe[src]");
    for (const el of iframes) {
      if (isProtected(el)) continue;
      const src = el.src || "";
      if (AD_DOMAINS.some(d => src.includes(d))) {
        if (safeRemove(el, "ad-iframe-src: known ad domain")) {
          stats.adsRemoved++;
        }
      }
    }

    const anchors = limitQuery("a[href]");
    for (const el of anchors) {
      if (isProtected(el)) continue;
      const rect = el.getBoundingClientRect();
      if (rect.width > 300 || rect.height > 50) continue;
      const href = el.href || "";
      if (AD_DOMAINS.some(d => href.includes(d))) {
        if (safeRemove(el, "ad-link: known ad domain in href")) {
          stats.adsRemoved++;
        }
      }
    }

    const containers = divLike || limitQuery("div, section, aside");
    for (const el of containers) {
      if (isProtected(el)) continue;
      const links = el.querySelectorAll("a[href]");
      if (links.length < 2) continue;
      const adLinks = Array.from(links).filter(a =>
        AD_DOMAINS.some(d => (a.href || "").includes(d))
      );
      if (adLinks.length > 0 && adLinks.length >= links.length * 0.5) {
        if (safeRemove(el, "ad-container: " + adLinks.length + "/" + links.length + " ad links")) {
          stats.adsRemoved++;
        }
      }
    }
  } catch (e) {}
}

// ---------------------------------------------------------------------------
// 8. Position-based ad detector
// ---------------------------------------------------------------------------
function eatByPosition(candidates) {
  if (!EAT_POSITION) return;
  try {
    const els = candidates || limitQuery("div, section, aside");
    for (const el of els) {
      if (isProtected(el)) continue;
      const cls = (el.className || "") + " " + (el.id || "");
      const tokens = cls.trim().split(/\s+/);
      if (tokens.some(t => /^(header|footer|nav|menu|toolbar|sidebar|main|content|article)$/i.test(t))) continue;
      if (el.tagName === "HEADER" || el.tagName === "FOOTER" || el.tagName === "NAV") continue;

      const style = getComputedStyle(el);
      if (style.position !== "fixed" && style.position !== "sticky") continue;

      const rect = el.getBoundingClientRect();
      const vw = window.innerWidth;
      const vh = window.innerHeight;

      const isBottomBanner = rect.top >= vh - 130 && rect.left <= 20 && rect.right >= vw - 20 && rect.height <= 130;
      const isTopBanner    = rect.top <= 0 && rect.left <= 20 && rect.right >= vw - 20 && rect.height <= 130;
      const isRightRail    = rect.left >= vw - 200 && rect.right <= vw && rect.height > 80 && rect.height < 900 && rect.top > 0;
      const isCornerAd     = rect.left >= vw - 350 && rect.top >= vh - 350 && rect.width < 350 && rect.height < 350;

      const kind = isBottomBanner ? "bottom-banner" : isTopBanner ? "top-banner" : isRightRail ? "right-rail" : isCornerAd ? "corner-popup" : null;
      if (!kind) continue;
      if (visibleTextLength(el) > 80) continue;

      if (safeRemove(el, "ad-position: " + kind)) {
        stats.adsRemoved++;
      }
    }
  } catch (e) {}
}

// ---------------------------------------------------------------------------
// 9. ARIA / accessibility label ad detector
// ---------------------------------------------------------------------------
function eatByAria() {
  if (!EAT_ARIA) return;
  try {
    const ariaKeywords = ["advertisement", "ad", "sponsored", "promoted", "advertising", "promotion"];
    for (const kw of ariaKeywords) {
      try {
        const elements = document.querySelectorAll(`[aria-label*="${kw}" i]`);
        for (const el of elements) {
          if (safeRemove(el, "aria-label: " + kw)) {
            stats.adsRemoved++;
          }
        }
      } catch {}
      try {
        const elements = document.querySelectorAll(`[aria-describedby*="${kw}" i]`);
        for (const el of elements) {
          if (safeRemove(el, "aria-describedby: " + kw)) {
            stats.adsRemoved++;
          }
        }
      } catch {}
    }
    try {
      const roles = document.querySelectorAll('[role="complementary"]');
      for (const el of roles) {
        const str = (el.className || "") + (el.id || "");
        if (/(^|\s)ad($|\s)|sponsor|promot/i.test(str)) {
          if (safeRemove(el, "role=complementary + ad class/id")) {
            stats.adsRemoved++;
          }
        }
      }
    } catch {}
  } catch (e) {}
}

// ---------------------------------------------------------------------------
// 10. Tab-nap link neutralizer
// ---------------------------------------------------------------------------
function napPatrol() {
  if (!EAT_POPUPS) return;
  try {
    const SAFE_SHARE_DOMAINS = [
      "facebook.com", "twitter.com", "x.com", "linkedin.com",
      "whatsapp.com", "telegram.me", "t.me", "reddit.com",
      "pinterest.com", "tumblr.com", "instagram.com",
      "youtube.com", "youtu.be", "snapchat.com",
      "discord.com", "discord.gg", "slack.com",
      "mailto:", "tel:",
      "forms.gle", "docs.google.com", "accounts.google.com",
      "play.google.com", "apps.apple.com",
      "github.com", "gitlab.com", "stackoverflow.com",
      "wikipedia.org", "archive.org",
    ];
    const all = limitQuery("a[target='_blank'], a[target='_new']");
    for (const a of all) {
      if (!a.href || a.href === "#" || a.href.startsWith("javascript:")) continue;
      const hrefLower = a.href.toLowerCase();
      if (SAFE_SHARE_DOMAINS.some(d => hrefLower.includes(d))) continue;
      if (hrefLower.includes(".gov") || hrefLower.includes(".int") || hrefLower.includes("itunes.apple.com")) continue;
      const hasText = (a.textContent || "").trim().length > 0;
      if (hasText) continue;
      const rect = a.getBoundingClientRect();
      const isInvisible = (rect.width <= 1 && rect.height <= 1);
      const isOffscreen = rect.right < 0 || rect.bottom < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
      const isZeroOpacity = getComputedStyle(a).opacity < 0.05;
      if (isInvisible && (isOffscreen || isZeroOpacity)) {
        a.removeAttribute("href");
        a.style.pointerEvents = "none";
        a.style.display = "none";
        stats.heavyElementsRemoved++;
      }
    }
  } catch {}
}

// ---------------------------------------------------------------------------
// 11. Ad bait detection — disguised native ads and clickbait
// ---------------------------------------------------------------------------
function eatBaitAds() {
  if (!ENABLE_BAIT) return;
  try {
    const candidates = limitQuery("div, section, aside, article, span, figure, a");
    for (const el of candidates) {
      if (isProtected(el)) continue;

      const text = (el.textContent || "").trim().toLowerCase();
      const cls = (el.className || "").toLowerCase();
      const id = (el.id || "").toLowerCase();
      const ariaLabel = (el.getAttribute("aria-label") || "").toLowerCase();
      const combinedStr = cls + " " + id + " " + ariaLabel;

      const nativeLabels = ["sponsored", "promoted", "promotion", "advertisement", "advertising", "paid"];
      const isNative = nativeLabels.some(label =>
        combinedStr.includes(label) || text.includes(label)
      );
      if (isNative) {
        const rect = el.getBoundingClientRect();
        if (rect.width > 0 && rect.height > 0 && rect.width < 800 && rect.height < 600) {
          if (safeRemove(el, "bait: native sponsored ad")) {
            stats.baitRemoved++;
            continue;
          }
        }
      }

      const recPhrases = [
        "around the web", "from around the web",
        "you may like", "you might also like",
        "recommended for you", "recommended stories",
        "sponsored content", "sponsored stories", "sponsored links",
        "promoted stories", "promoted content", "promoted links",
        "native advertising", "native ads",
        "from the web", "more from the web",
        "popular on the web", "trending now",
        "what's hot", "hot stories", "hot topics",
        "sponsored by", "brought to you by", "presented by",
        "in partnership with", "supported by",
      ];
      const isRec = recPhrases.some(phrase => text.includes(phrase));
      if (isRec) {
        const rect = el.getBoundingClientRect();
        if (rect.width > 0 && rect.height > 0 && rect.height < 500) {
          if (safeRemove(el, "bait: content recommendation widget")) {
            stats.baitRemoved++;
            continue;
          }
        }
      }

      // Limit to 10 anchors: full querySelectorAll on 500 candidates is O(n²).
      const linkEls = el.tagName === "A" ? [el] : Array.from(el.querySelectorAll("a")).slice(0, 10);
      for (const linkEl of linkEls) {
        const linkText = (linkEl.textContent || "").trim().toLowerCase();
        if (linkText.length < 10) continue;

        const clickbaitPatterns = [
          "you won't believe", "you'll never believe", "you won't guess",
          "mind blowing", "mind-blowing", "blow your mind",
          "this is what happens", "what happens next",
          "the reason why", "here's why", "here's what",
          "doctors hate", "doctors don't want", "doctors won't tell",
          "shocked to see", "shocked to find", "in shock",
          "one weird trick", "this one trick", "simple trick",
          "they don't want you to know",
          "what she did next", "what happened next",
          "number 1 will", "number 5 will", "number 7 will",
          "top 10", "top 5", "top 3",
          "amazing result", "unbelievable result", "unexpected result",
          "this will change", "changed forever", "never expected",
          "jaw dropping", "jaw-dropping", "absolutely incredible",
          "can't handle", "can't believe", "can't imagine",
          "this is why", "the real reason", "the truth about",
          "what you need to know", "what everyone needs",
          "so easy", "so simple", "effortless",
          "dropped to $", "only $", "for just $",
          "100% free", "totally free", "completely free",
          "viral video", "gone viral", "going viral",
          "must see", "must watch", "must read",
          "omg!", "wtf",
        ];
        const isClickbait = clickbaitPatterns.some(p => linkText.includes(p));
        if (isClickbait) {
          const rect = el.getBoundingClientRect();
          if (rect.width < 400 && rect.height < 400) {
            if (safeRemove(el, "bait: clickbait link")) {
              stats.baitRemoved++;
              break;
            }
          }
        }
      }
    }
  } catch (e) {}
}

// ---------------------------------------------------------------------------
// eatAll — master sweep orchestrator
// ---------------------------------------------------------------------------
function eatAll() {
  sweepCount++;
  // Precompute once — eatHeavy, eatEmptyPlaceholders, eatByPosition, and the
  // container section of eatByLinkDestination all query the same div/section/aside
  // set. Computing it four times separately doubled DOM traversal cost on large pages.
  const divLike = limitQuery("div, section, aside");
  eatAds();
  eatTrackers();
  eatAutoplay();
  eatHeavy(divLike);
  eatEmptyPlaceholders(divLike);
  if (sweepCount % 3 === 0) eatByTextContent();
  if (sweepCount === 1 || sweepCount % 5 === 0) eatByLinkDestination(divLike);
  if (sweepCount % 2 === 0) eatByPosition(divLike);
  if (sweepCount % 5 === 0) eatByAria();
  eatBaitAds();
  if (stats.adsRemoved > 0 || stats.trackersRemoved > 0 ||
      stats.videosPaused > 0 || stats.heavyElementsRemoved > 0 ||
      stats.baitRemoved > 0) {
    scheduleReport();
  }
}
