import {
  isRetrievalTier
} from "./chunk-SOBJ6NEY.js";
import {
  summarizeDisclosureTokens
} from "./chunk-USFPPRAF.js";

// src/recall-explain-renderer.ts
function sanitizeString(v) {
  return typeof v === "string" && v.length > 0 ? v : null;
}
function sanitizeFiniteNumber(v) {
  return typeof v === "number" && Number.isFinite(v) ? v : null;
}
function normalizeTierExplain(value) {
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
  const raw = value;
  const filteredBy = Array.isArray(raw.filteredBy) ? raw.filteredBy.filter((x) => typeof x === "string") : [];
  const sourceAnchors = Array.isArray(raw.sourceAnchors) ? raw.sourceAnchors.filter(
    (a) => !!a && typeof a === "object" && typeof a.path === "string"
  ).map((a) => {
    const lr = a.lineRange;
    const lineRange = Array.isArray(lr) && lr.length === 2 && Number.isFinite(lr[0]) && Number.isFinite(lr[1]) ? [lr[0], lr[1]] : void 0;
    return lineRange ? { path: a.path, lineRange } : { path: a.path };
  }) : void 0;
  return {
    tier: isRetrievalTier(raw.tier) ? raw.tier : "hybrid",
    tierReason: typeof raw.tierReason === "string" ? raw.tierReason : "",
    filteredBy,
    candidatesConsidered: sanitizeFiniteNumber(raw.candidatesConsidered) ?? 0,
    latencyMs: sanitizeFiniteNumber(raw.latencyMs) ?? 0,
    ...sourceAnchors !== void 0 ? { sourceAnchors } : {}
  };
}
function toRecallExplainJson(snapshot) {
  if (!snapshot) {
    return {
      hasExplain: false,
      snapshotFound: false,
      sessionKey: null,
      recordedAt: null,
      namespace: null,
      memoryIds: [],
      source: null,
      sourcesUsed: null,
      latencyMs: null,
      tierExplain: null
    };
  }
  const normalizedExplain = normalizeTierExplain(snapshot.tierExplain);
  return {
    hasExplain: normalizedExplain !== null,
    snapshotFound: true,
    sessionKey: sanitizeString(snapshot.sessionKey),
    recordedAt: sanitizeString(snapshot.recordedAt),
    namespace: sanitizeString(snapshot.namespace),
    memoryIds: Array.isArray(snapshot.memoryIds) ? snapshot.memoryIds.filter((x) => typeof x === "string") : [],
    source: sanitizeString(snapshot.source),
    sourcesUsed: Array.isArray(snapshot.sourcesUsed) ? snapshot.sourcesUsed.filter((x) => typeof x === "string") : null,
    latencyMs: sanitizeFiniteNumber(snapshot.latencyMs),
    tierExplain: normalizedExplain
  };
}
function renderTierExplainTextLines(tierExplain) {
  const lines = [];
  if (!tierExplain) {
    lines.push(
      "(not populated \u2014 direct-answer tier disabled or did not fire)"
    );
    return lines;
  }
  lines.push(`tier: ${tierExplain.tier}`);
  lines.push(`reason: ${tierExplain.tierReason}`);
  lines.push(`candidates-considered: ${tierExplain.candidatesConsidered}`);
  lines.push(`latency-ms: ${tierExplain.latencyMs}`);
  if (tierExplain.filteredBy.length > 0) {
    lines.push(`filtered-by: ${tierExplain.filteredBy.join(", ")}`);
  } else {
    lines.push("filtered-by: (none)");
  }
  if (tierExplain.sourceAnchors && tierExplain.sourceAnchors.length > 0) {
    lines.push("source-anchors:");
    for (const anchor of tierExplain.sourceAnchors) {
      const range = anchor.lineRange ? `:${anchor.lineRange[0]}-${anchor.lineRange[1]}` : "";
      lines.push(`  - ${anchor.path}${range}`);
    }
  }
  return lines;
}
function toRecallExplainText(snapshot) {
  const lines = ["=== Recall Explain ==="];
  if (!snapshot) {
    lines.push("No recall snapshot recorded yet.");
    return lines.join("\n");
  }
  const sessionKey = sanitizeString(snapshot.sessionKey);
  const recordedAt = sanitizeString(snapshot.recordedAt);
  const namespace = sanitizeString(snapshot.namespace);
  const source = sanitizeString(snapshot.source);
  lines.push(`session: ${sessionKey ?? "(unknown)"}`);
  lines.push(`recorded: ${recordedAt ?? "(unknown)"}`);
  if (namespace) lines.push(`namespace: ${namespace}`);
  if (source) lines.push(`source: ${source}`);
  const sourcesUsed = Array.isArray(snapshot.sourcesUsed) ? snapshot.sourcesUsed.filter((x) => typeof x === "string") : [];
  if (sourcesUsed.length > 0) {
    lines.push(`sources-used: ${sourcesUsed.join(", ")}`);
  }
  const latencyMs = sanitizeFiniteNumber(snapshot.latencyMs);
  if (latencyMs !== null) {
    lines.push(`latency-ms: ${latencyMs}`);
  }
  const memoryIds = Array.isArray(snapshot.memoryIds) ? snapshot.memoryIds.filter((x) => typeof x === "string") : [];
  if (memoryIds.length > 0) {
    lines.push(`memories: ${memoryIds.join(", ")}`);
  }
  const ex = normalizeTierExplain(snapshot.tierExplain);
  if (!ex) {
    lines.push("");
    lines.push(
      "tier-explain: (not populated \u2014 direct-answer tier disabled or did not fire)"
    );
    return lines.join("\n");
  }
  lines.push("");
  lines.push("--- tier explain ---");
  for (const line of renderTierExplainTextLines(ex)) {
    lines.push(line);
  }
  return lines.join("\n");
}
function sanitizeForMarkdownInline(value) {
  if (typeof value !== "string") return "";
  return value.replace(/[`|\r\n]/g, " ").trim();
}
function mapLegacySourceToServedBy(source) {
  if (source === "recent_scan") return "recent-scan";
  return "hybrid";
}
function toRecallXraySnapshotFromLegacy(snapshot) {
  if (!snapshot) return null;
  const capturedAt = (() => {
    if (typeof snapshot.recordedAt !== "string") return 0;
    const ms = Date.parse(snapshot.recordedAt);
    return Number.isFinite(ms) && ms >= 0 ? ms : 0;
  })();
  const memoryIds = Array.isArray(snapshot.memoryIds) ? snapshot.memoryIds.filter((x) => typeof x === "string") : [];
  const servedBy = mapLegacySourceToServedBy(snapshot.source);
  const results = memoryIds.map((memoryId) => ({
    memoryId,
    path: "",
    servedBy,
    scoreDecomposition: { final: 0 },
    admittedBy: []
  }));
  const filters = [];
  return {
    schemaVersion: "1",
    // `LastRecallSnapshot` does not preserve the original query text;
    // synthesize a placeholder so the renderer has a non-empty
    // string to print.  `queryHash` + `queryLen` stay in the JSON
    // payload via `toRecallExplainJson` for callers that need them.
    query: snapshot.queryHash ? `(legacy explain; queryHash=${snapshot.queryHash})` : "(legacy explain)",
    // `snapshotId` is synthesized here; `sessionKey` is already
    // sanitized before it reaches the ID because we re-use the
    // sanitized string below.
    snapshotId: `legacy-${sanitizeForMarkdownInline(snapshot.sessionKey ?? "unknown") || "unknown"}-${capturedAt}`,
    capturedAt,
    // Run the raw on-disk value through the same normalizer the text
    // and JSON paths use so the markdown adapter cannot render
    // unvalidated tier-explain payloads (cursor / codex review on
    // #605).  A malformed tierExplain is dropped to null, matching the
    // behavior of the non-markdown surfaces.
    tierExplain: normalizeTierExplain(snapshot.tierExplain) ?? null,
    results,
    filters,
    budget: { chars: 0, used: 0 },
    // Sanitize legacy session metadata at the adapter boundary so a
    // malformed on-disk value (containing backticks, pipes, or
    // newlines) cannot break the enclosing markdown table when
    // `renderXrayMarkdown` prints it in a raw code-span cell (codex P2
    // review on #605).
    ...snapshot.sessionKey ? (() => {
      const clean = sanitizeForMarkdownInline(snapshot.sessionKey);
      return clean ? { sessionKey: clean } : {};
    })() : {},
    ...snapshot.namespace ? (() => {
      const clean = sanitizeForMarkdownInline(snapshot.namespace);
      return clean ? { namespace: clean } : {};
    })() : {}
  };
}
function renderRecallExplain(snapshot, format) {
  if (format === "json") {
    return JSON.stringify(toRecallExplainJson(snapshot), null, 2);
  }
  if (format === "markdown") {
    return renderXrayMarkdown(toRecallXraySnapshotFromLegacy(snapshot));
  }
  return toRecallExplainText(snapshot);
}
function parseRecallExplainFormat(value) {
  if (value === void 0 || value === null) return "text";
  if (typeof value !== "string") {
    throw new Error(
      `--format expects "text", "json", or "markdown", got ${typeof value}`
    );
  }
  const v = value.trim().toLowerCase();
  if (v === "text" || v === "json" || v === "markdown") return v;
  throw new Error(
    `--format expects "text", "json", or "markdown", got ${JSON.stringify(value)}`
  );
}

// src/recall-xray-renderer.ts
var RECALL_XRAY_FORMATS = [
  "json",
  "text",
  "markdown"
];
function parseXrayFormat(value) {
  if (value === void 0 || value === null) return "text";
  if (typeof value !== "string") {
    throw new Error(
      `--format expects one of ${RECALL_XRAY_FORMATS.join(", ")}; got ${typeof value}`
    );
  }
  const v = value.trim().toLowerCase();
  if (v === "json" || v === "text" || v === "markdown") return v;
  throw new Error(
    `--format expects one of ${RECALL_XRAY_FORMATS.join(", ")}; got ${JSON.stringify(value)}`
  );
}
function renderXray(snapshot, format) {
  if (format === "json") return renderXrayJson(snapshot);
  if (format === "markdown") return renderXrayMarkdown(snapshot);
  return renderXrayText(snapshot);
}
function renderXrayJson(snapshot) {
  if (!snapshot) {
    return JSON.stringify(
      { schemaVersion: "1", snapshotFound: false },
      null,
      2
    );
  }
  return JSON.stringify(
    { snapshotFound: true, ...snapshot },
    null,
    2
  );
}
function renderXrayText(snapshot) {
  const lines = ["=== Recall X-ray ==="];
  if (!snapshot) {
    lines.push("No X-ray snapshot captured.");
    return lines.join("\n");
  }
  lines.push(`query: ${snapshot.query}`);
  lines.push(`snapshot-id: ${snapshot.snapshotId}`);
  lines.push(`captured-at: ${formatCapturedAt(snapshot.capturedAt)}`);
  if (snapshot.sessionKey) lines.push(`session: ${snapshot.sessionKey}`);
  if (snapshot.namespace) lines.push(`namespace: ${snapshot.namespace}`);
  if (snapshot.traceId) lines.push(`trace-id: ${snapshot.traceId}`);
  lines.push(
    `budget: ${snapshot.budget.used} / ${snapshot.budget.chars} chars`
  );
  lines.push("");
  lines.push("--- filters ---");
  if (snapshot.filters.length === 0) {
    lines.push("(no filter traces recorded)");
  } else {
    for (const f of snapshot.filters) {
      lines.push(renderFilterTextLine(f));
    }
  }
  lines.push("");
  lines.push("--- results ---");
  if (snapshot.results.length === 0) {
    lines.push("(no results admitted)");
  } else {
    snapshot.results.forEach((result, idx) => {
      for (const line of renderResultTextLines(result, idx + 1)) {
        lines.push(line);
      }
    });
  }
  lines.push("");
  lines.push("--- tier explain ---");
  for (const line of renderTierExplainTextLines(snapshot.tierExplain ?? null)) {
    lines.push(line);
  }
  return lines.join("\n");
}
function renderFilterTextLine(f) {
  const base = `- ${f.name}: ${f.admitted}/${f.considered} admitted`;
  return f.reason ? `${base} (${f.reason})` : base;
}
function renderResultTextLines(result, rank) {
  const lines = [];
  lines.push(`[${rank}] ${result.memoryId} \u2014 ${servedByLabel(result.servedBy)}`);
  if (result.path) lines.push(`    path: ${result.path}`);
  lines.push(`    score: ${renderScoreDecomposition(result)}`);
  if (result.admittedBy.length > 0) {
    lines.push(`    admitted-by: ${result.admittedBy.join(", ")}`);
  }
  if (result.rejectedBy) {
    lines.push(`    rejected-by: ${result.rejectedBy}`);
  }
  if (result.graphPath && result.graphPath.length > 0) {
    lines.push(`    graph-path: ${result.graphPath.join(" -> ")}`);
    if (result.graphEdgeConfidences && result.graphEdgeConfidences.length > 0) {
      lines.push(
        `    edge-confidences: ${result.graphEdgeConfidences.map((c) => c.toFixed(2)).join(", ")}`
      );
    }
  }
  if (result.auditEntryId) {
    lines.push(`    audit-entry: ${result.auditEntryId}`);
  }
  return lines;
}
function renderXrayMarkdown(snapshot) {
  const lines = ["# Recall X-ray"];
  if (!snapshot) {
    lines.push("");
    lines.push("_No X-ray snapshot captured._");
    return lines.join("\n");
  }
  lines.push("");
  lines.push(`**Query:** ${mdInlineCode(snapshot.query)}`);
  lines.push("");
  lines.push("| Field | Value |");
  lines.push("| --- | --- |");
  lines.push(`| Snapshot ID | \`${snapshot.snapshotId}\` |`);
  lines.push(`| Captured at | ${formatCapturedAt(snapshot.capturedAt)} |`);
  if (snapshot.sessionKey) {
    lines.push(`| Session | \`${snapshot.sessionKey}\` |`);
  }
  if (snapshot.namespace) {
    lines.push(`| Namespace | \`${snapshot.namespace}\` |`);
  }
  if (snapshot.traceId) {
    lines.push(`| Trace ID | \`${snapshot.traceId}\` |`);
  }
  lines.push(
    `| Budget | ${snapshot.budget.used} / ${snapshot.budget.chars} chars |`
  );
  lines.push("");
  lines.push("## Filters");
  if (snapshot.filters.length === 0) {
    lines.push("");
    lines.push("_No filter traces recorded._");
  } else {
    lines.push("");
    lines.push("| Filter | Considered | Admitted | Reason |");
    lines.push("| --- | ---: | ---: | --- |");
    for (const f of snapshot.filters) {
      const reason = f.reason ? mdEscape(f.reason) : "";
      lines.push(`| ${mdEscape(f.name)} | ${f.considered} | ${f.admitted} | ${reason} |`);
    }
  }
  lines.push("");
  lines.push("## Results");
  if (snapshot.results.length === 0) {
    lines.push("");
    lines.push("_No results admitted._");
  } else {
    snapshot.results.forEach((result, idx) => {
      for (const line of renderResultMarkdownLines(result, idx + 1)) {
        lines.push(line);
      }
    });
    const summary = summarizeDisclosureTokens(snapshot.results);
    const hasAnyDisclosure = summary.chunk.count + summary.section.count + summary.raw.count > 0;
    if (hasAnyDisclosure) {
      lines.push("");
      lines.push("### Token spend by disclosure");
      lines.push("");
      lines.push("| Disclosure | Results | Estimated tokens |");
      lines.push("| --- | ---: | ---: |");
      lines.push(
        `| chunk | ${summary.chunk.count} | ${summary.chunk.estimatedTokens} |`
      );
      lines.push(
        `| section | ${summary.section.count} | ${summary.section.estimatedTokens} |`
      );
      lines.push(
        `| raw | ${summary.raw.count} | ${summary.raw.estimatedTokens} |`
      );
      if (summary.unspecified.count > 0) {
        lines.push(
          `| _(unspecified)_ | ${summary.unspecified.count} | ${summary.unspecified.estimatedTokens} |`
        );
      }
    }
  }
  lines.push("");
  lines.push("## Tier Explain");
  if (!snapshot.tierExplain) {
    lines.push("");
    lines.push(
      "_Not populated \u2014 direct-answer tier disabled or did not fire._"
    );
  } else {
    const te = snapshot.tierExplain;
    lines.push("");
    lines.push("| Field | Value |");
    lines.push("| --- | --- |");
    lines.push(`| Tier | \`${te.tier}\` |`);
    lines.push(`| Reason | ${mdEscape(te.tierReason)} |`);
    lines.push(`| Candidates considered | ${te.candidatesConsidered} |`);
    lines.push(`| Latency (ms) | ${te.latencyMs} |`);
    lines.push(
      `| Filtered by | ${te.filteredBy.length > 0 ? te.filteredBy.map(mdInlineCode).join(", ") : "_(none)_"} |`
    );
    if (te.sourceAnchors && te.sourceAnchors.length > 0) {
      lines.push("");
      lines.push("**Source anchors:**");
      for (const anchor of te.sourceAnchors) {
        const range = anchor.lineRange ? `:${anchor.lineRange[0]}-${anchor.lineRange[1]}` : "";
        lines.push(`- \`${anchor.path}${range}\``);
      }
    }
  }
  return lines.join("\n");
}
function renderResultMarkdownLines(result, rank) {
  const lines = [];
  lines.push("");
  lines.push(
    `### ${rank}. \`${result.memoryId}\` \u2014 ${servedByLabel(result.servedBy)}`
  );
  if (result.path) {
    lines.push("");
    lines.push(`- **Path:** \`${result.path}\``);
  } else {
    lines.push("");
  }
  lines.push(`- **Score:** ${renderScoreDecomposition(result)}`);
  if (result.admittedBy.length > 0) {
    lines.push(
      `- **Admitted by:** ${result.admittedBy.map(mdInlineCode).join(", ")}`
    );
  }
  if (result.rejectedBy) {
    lines.push(`- **Rejected by:** ${mdInlineCode(result.rejectedBy)}`);
  }
  if (result.graphPath && result.graphPath.length > 0) {
    lines.push(
      `- **Graph path:** ${result.graphPath.map(mdInlineCode).join(" \u2192 ")}`
    );
    if (result.graphEdgeConfidences && result.graphEdgeConfidences.length > 0) {
      lines.push(
        `- **Edge confidences:** ${result.graphEdgeConfidences.map((c) => mdInlineCode(c.toFixed(2))).join(", ")}`
      );
    }
  }
  if (result.auditEntryId) {
    lines.push(`- **Audit entry:** \`${result.auditEntryId}\``);
  }
  if (result.disclosure !== void 0) {
    const tokenLine = typeof result.estimatedTokens === "number" ? ` (~${result.estimatedTokens} tokens)` : "";
    lines.push(`- **Disclosure:** \`${result.disclosure}\`${tokenLine}`);
  } else if (typeof result.estimatedTokens === "number") {
    lines.push(`- **Estimated tokens:** ${result.estimatedTokens}`);
  }
  return lines;
}
function servedByLabel(servedBy) {
  return `served-by=${servedBy}`;
}
function renderScoreDecomposition(result) {
  const parts = [`final=${formatScore(result.scoreDecomposition.final)}`];
  const s = result.scoreDecomposition;
  if (s.vector !== void 0) parts.push(`vector=${formatScore(s.vector)}`);
  if (s.bm25 !== void 0) parts.push(`bm25=${formatScore(s.bm25)}`);
  if (s.importance !== void 0) {
    parts.push(`importance=${formatScore(s.importance)}`);
  }
  if (s.mmrPenalty !== void 0) {
    parts.push(`mmr_penalty=${formatScore(s.mmrPenalty)}`);
  }
  if (s.tierPrior !== void 0) {
    parts.push(`tier_prior=${formatScore(s.tierPrior)}`);
  }
  if (s.reinforcementBoost !== void 0 && s.reinforcementBoost > 0) {
    parts.push(`reinforcement_boost=${formatScore(s.reinforcementBoost)}`);
  }
  return parts.join(" ");
}
function formatScore(value) {
  if (!Number.isFinite(value)) return "0.0000";
  return value.toFixed(4);
}
function formatCapturedAt(ts) {
  if (!Number.isFinite(ts) || ts < 0) return "(unknown)";
  try {
    return new Date(ts).toISOString();
  } catch {
    return "(unknown)";
  }
}
function mdEscape(value) {
  return value.replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
}
function mdInlineCode(value) {
  if (value.length === 0) return "``";
  const longestRun = /`+/g;
  let maxLen = 0;
  for (const match of value.matchAll(longestRun)) {
    if (match[0].length > maxLen) maxLen = match[0].length;
  }
  const fence = "`".repeat(maxLen + 1);
  const pad = value.startsWith("`") || value.endsWith("`") ? " " : "";
  return `${fence}${pad}${value}${pad}${fence}`;
}

export {
  RECALL_XRAY_FORMATS,
  parseXrayFormat,
  renderXray,
  renderXrayJson,
  renderXrayText,
  renderXrayMarkdown,
  toRecallExplainJson,
  renderTierExplainTextLines,
  toRecallExplainText,
  toRecallXraySnapshotFromLegacy,
  renderRecallExplain,
  parseRecallExplainFormat
};
//# sourceMappingURL=chunk-LW2NMHDW.js.map