import { getRerankProvider } from "../providers/rerank-provider.js";
import { getConfig } from "../config.js";
import { createLogger } from "../logger.js";
import { parseHeadings, lineStartCharOffsets } from "../extraction/markdown.js";
const log = createLogger("search");
const MAX_PASSAGE_LENGTH = 500;
const MIN_PASSAGE_LENGTH = 50;
const DEFAULT_MAX_HIGHLIGHTS = 10;
function shouldKeep(trimmed) {
  if (trimmed.length < MIN_PASSAGE_LENGTH) return false;
  if (trimmed.startsWith("#")) return false;
  if (trimmed.startsWith("|")) return false;
  if (trimmed.startsWith("```")) return false;
  if (trimmed.startsWith("- ") && trimmed.length <= 120) return false;
  return true;
}
function splitIntoPassages(markdown) {
  if (!markdown) return [];
  const out = [];
  const re = /\n\n+/g;
  let blockStart = 0;
  let m;
  const consider = (rawStart, rawEnd) => {
    const raw = markdown.slice(rawStart, rawEnd);
    if (!raw) return;
    let leading = 0;
    while (leading < raw.length && /\s/.test(raw[leading])) leading++;
    let trailing = raw.length;
    while (trailing > leading && /\s/.test(raw[trailing - 1])) trailing--;
    if (trailing <= leading) return;
    const trimmedStart = rawStart + leading;
    const trimmedEnd = rawStart + trailing;
    const trimmed = markdown.slice(trimmedStart, trimmedEnd);
    if (!shouldKeep(trimmed)) return;
    const text = trimmed.length > MAX_PASSAGE_LENGTH ? trimmed.slice(0, MAX_PASSAGE_LENGTH) : trimmed;
    const charEnd = trimmedStart + text.length;
    out.push({ text, charStart: trimmedStart, charEnd });
  };
  while ((m = re.exec(markdown)) !== null) {
    consider(blockStart, m.index);
    blockStart = m.index + m[0].length;
  }
  consider(blockStart, markdown.length);
  return out;
}
function splitIntoPassageStrings(markdown) {
  return splitIntoPassages(markdown).map((p) => p.text);
}
function mapPassageHeadings(markdown, passages) {
  const lines = markdown.split("\n");
  const headings = parseHeadings(lines);
  const offsets = lineStartCharOffsets(lines);
  const headingOffsets = headings.map((h) => ({ text: h.text, charStart: offsets[h.lineIndex] }));
  return passages.map((p) => {
    let nearest = null;
    for (const h of headingOffsets) {
      if (h.charStart <= p.charStart) nearest = h.text;
      else break;
    }
    return { ...p, sectionHeading: nearest };
  });
}
async function extractHighlights(query, results, maxHighlights = DEFAULT_MAX_HIGHLIGHTS) {
  const citations = [];
  const candidates = [];
  for (let i = 0; i < results.length; i++) {
    const r = results[i];
    citations.push({
      index: i + 1,
      url: r.url,
      title: r.title,
      snippet: r.snippet
    });
    const source = r.markdown_content ?? r.snippet ?? "";
    const passages = splitIntoPassages(source);
    const annotated = mapPassageHeadings(source, passages);
    for (const p of annotated) {
      candidates.push({
        text: p.text,
        sourceIndex: i + 1,
        sourceUrl: r.url,
        sourceTitle: r.title,
        charStart: p.charStart,
        charEnd: p.charEnd,
        sectionHeading: p.sectionHeading
      });
    }
  }
  if (candidates.length === 0) {
    return {
      highlights: fallbackHighlights(results, maxHighlights),
      citations,
      reranker_used: false
    };
  }
  const cfg = getConfig();
  if (cfg.reranker === "onnx") {
    try {
      const provider = await getRerankProvider();
      const scored = await provider.rerank(
        query,
        candidates.map((c, i) => ({ id: String(i), text: c.text }))
      );
      if (scored.length > 0) {
        const ranked = scored.slice(0, maxHighlights);
        const highlights = ranked.map((s) => {
          const cand = candidates[Number(s.id)];
          return {
            text: cand.text,
            source_index: cand.sourceIndex,
            relevance_score: s.score,
            source_url: cand.sourceUrl,
            source_title: cand.sourceTitle,
            section_heading: cand.sectionHeading,
            source_span: { start: cand.charStart, end: cand.charEnd }
          };
        });
        return { highlights, citations, reranker_used: true };
      }
    } catch (err) {
      log.debug("rerank provider failed, using fallback passages", { error: String(err) });
    }
  }
  return { highlights: fallbackHighlights(results, maxHighlights), citations, reranker_used: false };
}
function fallbackHighlights(results, maxHighlights) {
  const out = [];
  for (let i = 0; i < results.length && out.length < maxHighlights; i++) {
    const r = results[i];
    const source = r.markdown_content ?? "";
    const passages = source ? splitIntoPassages(source) : [];
    if (passages.length > 0) {
      const annotated = mapPassageHeadings(source, [passages[0]])[0];
      const text2 = annotated.text.slice(0, MAX_PASSAGE_LENGTH);
      out.push({
        text: text2,
        source_index: i + 1,
        relevance_score: r.relevance_score,
        source_url: r.url,
        source_title: r.title,
        section_heading: annotated.sectionHeading,
        source_span: { start: annotated.charStart, end: annotated.charStart + text2.length }
      });
      continue;
    }
    const snippet = r.snippet ?? "";
    if (!snippet) continue;
    const text = snippet.slice(0, MAX_PASSAGE_LENGTH);
    out.push({
      text,
      source_index: i + 1,
      relevance_score: r.relevance_score,
      source_url: r.url,
      source_title: r.title,
      section_heading: null,
      source_span: { start: 0, end: text.length }
    });
  }
  return out;
}
export {
  extractHighlights,
  fallbackHighlights,
  mapPassageHeadings,
  splitIntoPassages
};
//# sourceMappingURL=highlights.js.map