import { parseHTML } from "linkedom";
import { htmlToMarkdown } from "../markdown.js";
const THREAD_URL_RE = /\/r\/[^/]+\/comments\/[^/]+/;
const REDDIT_BLOCK_PATTERNS = [
  /blocked by network security/i,
  /whoa there,\s*partner/i,
  /our cdn was unable to reach our servers/i,
  /access to this page has been denied/i
];
function detectAntiBotBlock(html) {
  if (!html) return null;
  const sample = html.slice(0, 1e4);
  for (const re of REDDIT_BLOCK_PATTERNS) {
    if (re.test(sample)) return "blocked";
  }
  return null;
}
function isRedditHost(hostname) {
  return hostname === "reddit.com" || hostname === "redd.it" || hostname.endsWith(".reddit.com") || hostname.endsWith(".redd.it");
}
function parseScoreFromTitleAttr(el) {
  if (!el) return 0;
  const titleAttr = el.getAttribute("title");
  if (titleAttr && /^-?\d+$/.test(titleAttr)) {
    return parseInt(titleAttr, 10);
  }
  const text = el.textContent?.trim() ?? "";
  const m = text.match(/-?\d+/);
  return m ? parseInt(m[0], 10) : 0;
}
function textOrFallback(el, fallback) {
  const t = el?.textContent?.trim() ?? "";
  return t || fallback;
}
function normalizeDeletedMarker(md) {
  return md.replace(/\\\[removed\\\]/gi, "[removed]").replace(/\\\[deleted\\\]/gi, "[deleted]");
}
function extractPostBodyMarkdown(thingEl) {
  const bodyEl = thingEl.querySelector(".expando .usertext-body .md, .expando .md");
  if (!bodyEl) {
    return "[deleted]";
  }
  const md = normalizeDeletedMarker(htmlToMarkdown(bodyEl.innerHTML).trim());
  if (!md) return "[deleted]";
  return md;
}
function parseComment(commentEl) {
  const isDeleted = commentEl.classList.contains("deleted");
  const dataAuthor = commentEl.getAttribute("data-author") ?? "";
  const author = dataAuthor || textOrFallback(commentEl.querySelector(".tagline .author"), "[deleted]");
  const scoreAttr = commentEl.getAttribute("data-score-likes");
  let score = scoreAttr && /^-?\d+$/.test(scoreAttr) ? parseInt(scoreAttr, 10) : NaN;
  if (Number.isNaN(score)) {
    score = parseScoreFromTitleAttr(commentEl.querySelector(".tagline .score"));
  }
  const bodyEl = commentEl.querySelector(":scope > .entry .usertext-body .md");
  let body;
  if (isDeleted) {
    body = "[deleted]";
  } else if (!bodyEl) {
    body = "[deleted]";
  } else {
    body = normalizeDeletedMarker(htmlToMarkdown(bodyEl.innerHTML).trim()) || "[deleted]";
  }
  return {
    author: isDeleted ? "[deleted]" : author || "[deleted]",
    body,
    score: Number.isFinite(score) ? score : 0,
    replies: []
  };
}
function parseTopLevelComments(document) {
  const containers = document.querySelectorAll(".commentarea > .sitetable.nestedlisting");
  const out = [];
  for (const container of Array.from(containers)) {
    for (const node of Array.from(container.children)) {
      if (!node.classList?.contains("comment")) continue;
      out.push(parseComment(node));
    }
  }
  if (out.length === 0) {
    const fallback = document.querySelectorAll(".commentarea .thing.comment");
    for (const node of Array.from(fallback)) {
      out.push(parseComment(node));
    }
  }
  return out;
}
function parseAwards(thingEl) {
  const bar = thingEl.querySelector(".awardings-bar");
  if (!bar) return [];
  const raw = bar.textContent?.trim() ?? "";
  if (!raw) return [];
  return raw.split("|").map((s) => s.trim()).filter(Boolean);
}
function parseUpvoteRatio(thingEl) {
  const attr = thingEl.getAttribute("data-upvote-ratio") ?? thingEl.getAttribute("data-percent") ?? "";
  if (!attr) return 1;
  const n = parseFloat(attr);
  if (!Number.isFinite(n)) return 1;
  if (n > 1) return Math.max(0, Math.min(1, n / 100));
  return Math.max(0, Math.min(1, n));
}
function parsePostedAt(thingEl) {
  const timeEl = thingEl.querySelector("time[datetime]");
  return timeEl?.getAttribute("datetime")?.trim() ?? "";
}
function parseThreadCore(html) {
  const { document } = parseHTML(html);
  const thingEl = document.querySelector(
    "#siteTable .thing.link, #siteTable .thing"
  );
  if (!thingEl) return null;
  const subreddit = thingEl.getAttribute("data-subreddit") ?? textOrFallback(thingEl.querySelector(".subreddit"), "").replace(/^\/r\//, "");
  if (!subreddit) return null;
  const linkTitleEl = thingEl.querySelector("a.title, p.title a");
  const ogTitleEl = document.querySelector('meta[property="og:title"]');
  const docTitleEl = document.querySelector("title");
  const titleCandidate = linkTitleEl?.textContent?.trim() || ogTitleEl?.getAttribute("content")?.trim() || docTitleEl?.textContent?.split(":")[0]?.trim() || "";
  const title = titleCandidate;
  const dataAuthor = thingEl.getAttribute("data-author") ?? "";
  const author = dataAuthor || textOrFallback(thingEl.querySelector(".tagline .author"), "[deleted]");
  const body_markdown = extractPostBodyMarkdown(thingEl);
  const score = parseScoreFromTitleAttr(thingEl.querySelector(".midcol .score, .score.likes"));
  const upvote_ratio = parseUpvoteRatio(thingEl);
  const awards = parseAwards(thingEl);
  const posted_at = parsePostedAt(thingEl);
  const comments = parseTopLevelComments(document);
  return {
    title,
    author: author || "[deleted]",
    subreddit,
    body_markdown,
    comments,
    score,
    upvote_ratio,
    awards,
    posted_at
  };
}
function extractRedditThread(html, _url) {
  if (!html) return null;
  const thread = parseThreadCore(html);
  if (!thread) return null;
  thread.comments = [...thread.comments].sort((a, b) => b.score - a.score);
  return thread;
}
function buildMarkdown(thread) {
  const lines = [];
  lines.push(`# ${thread.title}`);
  lines.push("");
  const headerBits = [`Subreddit: r/${thread.subreddit}`, `Author: u/${thread.author}`];
  if (Number.isFinite(thread.score)) headerBits.push(`Score: ${thread.score}`);
  if (thread.upvote_ratio !== void 0) headerBits.push(`Upvote ratio: ${thread.upvote_ratio}`);
  if (thread.posted_at) headerBits.push(`Posted: ${thread.posted_at}`);
  if (thread.awards.length > 0) headerBits.push(`Awards: ${thread.awards.join(", ")}`);
  lines.push(headerBits.join(" | "));
  lines.push("");
  lines.push(thread.body_markdown || "[deleted]");
  lines.push("");
  if (thread.comments.length > 0) {
    lines.push("## Comments");
    lines.push("");
    for (const c of thread.comments) {
      lines.push(`### u/${c.author} \u2014 ${c.score} points`);
      lines.push("");
      lines.push(c.body);
      lines.push("");
    }
  }
  return lines.join("\n").trim();
}
const redditExtractor = {
  name: "reddit",
  canHandle(url) {
    try {
      const u = new URL(url);
      if (!isRedditHost(u.hostname)) return false;
      if (u.hostname === "redd.it" || u.hostname.endsWith(".redd.it")) return true;
      return THREAD_URL_RE.test(u.pathname);
    } catch {
      return false;
    }
  },
  extract(html, url) {
    if (!html) return null;
    if (detectAntiBotBlock(html)) return null;
    const thread = extractRedditThread(html, url);
    if (!thread) return null;
    if (!thread.title) return null;
    const markdown = buildMarkdown(thread);
    return {
      title: thread.title,
      markdown,
      metadata: {
        author: thread.author,
        date: thread.posted_at || void 0,
        keywords: [`r/${thread.subreddit}`]
      },
      links: [],
      images: [],
      extractor: "site-specific",
      // Structured thread record threaded outward so routedExtract can surface
      // it on the FetchOutput without re-parsing the HTML a second time.
      site_data: thread
    };
  }
};
export {
  detectAntiBotBlock,
  extractRedditThread,
  redditExtractor
};
//# sourceMappingURL=reddit.js.map