const { decodeEntities, sanitizeText } = require("../html_extractor");
const { decodeBody } = require("../page_fetcher");
const { evaluateNetworkTarget } = require("../url_policy");

class BingRssSearchAdapter {
  constructor(options = {}) {
    this.fetcher = options.fetcher;
    this.id = "bing_rss";
  }

  async search(query, options = {}) {
    const endpoint = new URL("https://www.bing.com/search");
    endpoint.searchParams.set("q", query);
    endpoint.searchParams.set("format", "rss");
    endpoint.searchParams.set("count", String(Math.max(options.settings.max_results * 2, 10)));
    const policy = await evaluateNetworkTarget(endpoint.href, { resolveHost: this.fetcher.resolveHost });
    if (!policy.allowed) throw new Error(`Search source blocked: ${policy.reason}.`);
    const response = await this.fetcher.request(policy.url, options.settings, {
      networkOnly: true,
      timeoutMs: options.settings.search_timeout_ms,
      maxBytes: Math.min(options.settings.max_fetch_bytes, 2 * 1024 * 1024),
      accept: "application/rss+xml,application/xml,text/xml"
    });
    if (!response.ok) throw new Error(`Bing RSS search failed (${response.status}).`);
    const xml = decodeBody(
      response.body,
      response.headers.get("content-encoding"),
      Math.min(options.settings.max_fetch_bytes, 2 * 1024 * 1024)
    ).toString("utf8");
    return parseBingRss(xml).slice(0, Math.max(options.settings.max_results * 2, 10));
  }
}

function parseBingRss(xml) {
  return [...String(xml || "").matchAll(/<item\b[^>]*>([\s\S]*?)<\/item>/gi)]
    .map((match, index) => {
      const item = match[1];
      const url = tagValue(item, "link");
      if (!/^https?:\/\//i.test(url)) return null;
      return {
        title: sanitizeText(tagValue(item, "title"), 240),
        url,
        snippet: sanitizeText(tagValue(item, "description"), 900),
        date: normalizeDate(tagValue(item, "pubDate")),
        rank: index + 1,
        source: "bing_rss",
        raw_source_id: `bing-${index + 1}`
      };
    })
    .filter(Boolean);
}

function tagValue(value, tag) {
  const match = String(value).match(new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)<\\/${tag}>`, "i"));
  return decodeEntities(String(match?.[1] || "").replace(/^<!\[CDATA\[|\]\]>$/g, "")).trim();
}

function normalizeDate(value) {
  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? null : date.toISOString();
}

module.exports = { BingRssSearchAdapter, parseBingRss };
