const { sanitizeText } = require("./html_extractor");
const { sourceLinksEnabled } = require("./settings");

const REASONS = new Set([
  "fact_lookup",
  "resource_lookup",
  "troubleshooting",
  "documentation_lookup",
  "news_or_recent",
  "general_lookup"
]);

function formatResults(rows, options = {}) {
  const reason = REASONS.has(options.reason) ? options.reason : "general_lookup";
  const origin = normalizeOrigin(options.origin);
  const budget = outputBudget(options.settings, origin);
  const sourceLimit = origin === "twitch" ? 1 : origin === "discord" ? 4 : options.settings.max_results;
  const maximum = Math.max(1, Math.min(
    options.settings.max_results,
    Number(options.maxResults) || options.settings.max_results,
    sourceLimit
  ));
  const snippetLimit = reason === "fact_lookup" ? 180
    : ["resource_lookup", "troubleshooting", "documentation_lookup"].includes(reason) ? 320
      : 260;
  const ordered = prioritize(rows, reason).slice(0, maximum);
  const results = [];
  let used = 0;
  for (const [index, row] of ordered.entries()) {
    const url = sourceLinksEnabled(options.settings, origin) ? row.url : null;
    const normalized = {
      title: sanitizeText(row.title, 180),
      url,
      domain: safeDomain(row.url),
      snippet: sanitizeText(row.snippet, snippetLimit),
      date: row.date || null,
      rank: Number(row.rank) || index + 1,
      source: sanitizeText(row.source || inferSourceType(row.url), 80),
      raw_source_id: sanitizeText(row.raw_source_id || "", 120) || null,
      relevance_score: Number.isFinite(Number(row.relevance_score)) ? Number(row.relevance_score) : null,
      allowed: true
    };
    const cost = normalized.title.length + normalized.snippet.length + (normalized.url?.length || 0) + 40;
    if (results.length && used + cost > budget) break;
    results.push(normalized);
    used += cost;
  }
  const condensed = buildCondensedText(results, reason, origin, budget);
  return {
    reason,
    origin,
    output_budget_chars: budget,
    truncated: ordered.length > results.length || condensed.truncated,
    condensed_text: condensed.text,
    results
  };
}

function formatFetchedPage(page, options = {}) {
  const origin = normalizeOrigin(options.origin);
  const budget = outputBudget(options.settings, origin);
  const textBudget = Math.max(200, Math.min(options.settings.max_extracted_chars, budget));
  return {
    url: sourceLinksEnabled(options.settings, origin) ? page.url : null,
    final_url: sourceLinksEnabled(options.settings, origin) ? page.final_url : null,
    title: sanitizeText(page.title, 240),
    description: sanitizeText(page.description, 500),
    extracted_text: sanitizeText(page.extracted_text, textBudget),
    content_type: page.content_type,
    fetched_at: page.fetched_at,
    extraction_status: page.extraction_status,
    published_at: page.published_at || null,
    updated_at: page.updated_at || null,
    truncated: page.extracted_text.length > textBudget || page.truncated === true
  };
}

function buildCondensedText(results, reason, origin, budget) {
  const lines = results.map((result, index) => {
    const date = result.date ? ` (${result.date.slice(0, 10)})` : "";
    const source = result.url ? ` ${result.url}` : ` [${result.domain}]`;
    if (reason === "fact_lookup") return `${index + 1}. ${result.snippet || result.title}${date}${source}`;
    return `${index + 1}. ${result.title}${date}: ${result.snippet}${source}`;
  });
  let text = lines.join(origin === "twitch" ? " | " : "\n");
  let truncated = false;
  if (text.length > budget) {
    text = `${text.slice(0, Math.max(0, budget - 3)).trimEnd()}...`;
    truncated = true;
  }
  return { text, truncated };
}

function prioritize(rows, reason) {
  const values = [...rows];
  if (["documentation_lookup", "troubleshooting"].includes(reason)) {
    values.sort((left, right) =>
      authorityScore(right) - authorityScore(left) ||
      Number(right.relevance_score || 0) - Number(left.relevance_score || 0) ||
      Number(left.rank || 999) - Number(right.rank || 999)
    );
  } else if (reason === "news_or_recent") {
    values.sort((left, right) =>
      dateValue(right.date) - dateValue(left.date) ||
      Number(right.relevance_score || 0) - Number(left.relevance_score || 0)
    );
  }
  return values;
}

function outputBudget(settings, origin) {
  return Number(settings[`${origin}_output_chars`]) ||
    Number(settings.other_output_chars) ||
    700;
}

function normalizeOrigin(value) {
  const origin = String(value || "other").toLowerCase();
  return ["webui", "discord", "twitch", "youtube", "kick"].includes(origin) ? origin : "other";
}

function safeDomain(value) {
  try { return new URL(value).hostname; }
  catch { return ""; }
}

function inferSourceType(value) {
  const domain = safeDomain(value);
  if (/^(docs|developer|support)\./i.test(domain) || /\.(gov|edu)$/i.test(domain)) return "authoritative";
  if (/github\.com$|gitlab\.com$|huggingface\.co$|npmjs\.com$|pypi\.org$/i.test(domain)) return "repository";
  return "web";
}

function authorityScore(row) {
  const source = String(row.source || "").toLowerCase();
  return ["official", "authoritative", "documentation", "repository"].includes(source) ||
    inferSourceType(row.url) !== "web" ? 1 : 0;
}

function dateValue(value) {
  const timestamp = Date.parse(value);
  return Number.isFinite(timestamp) ? timestamp : 0;
}

module.exports = {
  REASONS,
  buildCondensedText,
  formatFetchedPage,
  formatResults,
  inferSourceType,
  normalizeOrigin,
  outputBudget,
  prioritize,
  safeDomain
};
