import { formatValue } from "./data.js";

/**
 * @typedef {Object} MetricRead
 * @property {string} label
 * @property {string | undefined} unit
 * @property {string} index
 * @property {number | string} start
 * @property {string | undefined} stamp
 * @property {unknown[]} values
 * @property {string} [requested]
 */

/**
 * @typedef {Object} SourceEvidence
 * @property {string} revision
 * @property {string} path
 * @property {number} startLine
 * @property {number} [endLine]
 */

const SOURCE_URL = "https://github.com/bitcoinresearchkit/brk/blob";

/** @param {SourceEvidence} source */
function sourceKey(source) {
  return `${source.revision}:${source.path}:${source.startLine}:${source.endLine ?? source.startLine}`;
}

/** @param {SourceEvidence} source */
function sourceLink(source) {
  const end = source.endLine && source.endLine !== source.startLine
    ? `-${source.endLine}`
    : "";
  const path = source.path.split("/").map(encodeURIComponent).join("/");
  const lines = `#L${source.startLine}${end ? `-L${source.endLine}` : ""}`;
  const url = `${SOURCE_URL}/${encodeURIComponent(source.revision)}/${path}${lines}`;
  return `[\`${source.path}:${source.startLine}${end}\`](${url})`;
}

/** @param {{ facts: string[], sources: SourceEvidence[], excerpts: (SourceEvidence & { content: string })[] }} evidence */
export function renderEvidence(evidence) {
  const sections = [...new Set(evidence.facts)].filter(Boolean);
  const cited = new Set();
  for (const excerpt of evidence.excerpts) {
    sections.push(`${sourceLink(excerpt)}\n\n\`\`\`\n${excerpt.content}\n\`\`\``);
    cited.add(sourceKey(excerpt));
  }
  const sources = [...new Map(
    evidence.sources.map((source) => [sourceKey(source), source]),
  ).values()].filter((source) => !cited.has(sourceKey(source)));
  if (sources.length) {
    sections.push(`Source${sources.length === 1 ? "" : "s"}: ${sources.map(sourceLink).join(", ")}`);
  }
  return sections.join("\n\n") || "I could not find enough verified evidence to answer that.";
}

/** @param {MetricRead[]} results */
export function renderData(results) {
  return results.map((result) => {
    const returnedPosition = result.index === "height"
      ? String(result.start)
      : result.stamp;
    if (
      result.requested &&
      returnedPosition &&
      result.requested !== returnedPosition
    ) {
      const returned = result.index === "height"
        ? `block ${returnedPosition}`
        : returnedPosition;
      return `**${result.label}**: no exact value was returned for ${result.requested}; the server returned ${returned} instead.`;
    }
    if (result.values.length === 1 && typeof result.values[0] === "number") {
      const position = result.index === "height"
        ? ` at block ${result.start}`
        : result.stamp
          ? ` at ${result.stamp}`
          : "";
      return `**${result.label}**: ${formatValue(result.values[0], result.unit)}${position}`;
    }
    const values = /** @type {number[]} */ (
      result.values.filter((value) => typeof value === "number")
    );
    if (!values.length) return `**${result.label}**: no values returned.`;
    return `**${result.label}**: ${values.length} values; latest ${formatValue(values[values.length - 1], result.unit)}.`;
  }).join("\n");
}

/** @param {string} answer @param {{ method: string, path: string }} operation */
export function renderApiAnswer(answer, operation) {
  return `${answer.trim()}\n\nData: \`${operation.method} ${operation.path}\``;
}
