import { stripAnsi } from "./ansi.js";
import { redactGitRemoteUrl } from "./git-remote.js";

/** Best-effort redaction when git/HTTP errors echo tokens. */
function redactSecretsFromLogSnippet(s: string): string {
  return s
    .replace(/ghs_[A-Za-z0-9]{20,}/g, "ghs_<redacted>")
    .replace(/ghp_[A-Za-z0-9]{20,}/g, "ghp_<redacted>")
    .replace(/github_pat_[A-Za-z0-9_]+/gi, "github_pat_<redacted>")
    .replace(/https:\/\/x-access-token:[^@\s]+@/gi, "https://");
}

/**
 * Max length for each line returned by the Datadog debug API. Agent CLI tool JSON in `msg`
 * often exceeds a few KB; a 2k cap truncated mid-JSON and looked like a parse error.
 */
const MAX_LINE_LENGTH = 64 * 1024;

/**
 * Strips ANSI, then if the line is Pino JSON extracts a short human line from `msg`
 * plus optional context (issueKey, jobKey, repo, branch).
 */
export function sanitizeLogLineForApi(raw: string): string {
  const s = stripAnsi(raw).trim();
  if (s.length === 0) return "";

  if (s.startsWith("{")) {
    try {
      const parsed: unknown = JSON.parse(s);
      if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
        const o = parsed as Record<string, unknown>;
        if (typeof o.msg === "string") {
          return truncate(formatPinoLine(o));
        }
      }
    } catch {
      // not JSON — keep plain text
    }
  }

  return truncate(s);
}

const ERROR_DETAIL_MAX = 500;

function formatPinoLine(o: Record<string, unknown>): string {
  const msg = o.msg;
  if (typeof msg !== "string") return "";

  const bits: string[] = [];
  if (typeof o.issueKey === "string" && o.issueKey.length > 0) bits.push(o.issueKey);
  if (typeof o.jobKey === "string" && o.jobKey.length > 0) bits.push(o.jobKey);
  if (typeof o.repo === "string" && o.repo.length > 0) bits.push(o.repo);
  if (typeof o.branch === "string" && o.branch.length > 0) bits.push(o.branch);

  const prefix = bits.length > 0 ? `${bits.join(" · ")} — ` : "";
  let line = `${prefix}${msg}`;

  // Append structured error context so failures are self-contained in each log line.
  const details: string[] = [];
  if (typeof o.step === "string" && o.step.length > 0) details.push(`step=${o.step}`);
  if (typeof o.remote === "string" && o.remote.length > 0) {
    details.push(`remote=${redactGitRemoteUrl(o.remote)}`);
  }
  if (typeof o.status === "number") details.push(`status=${o.status}`);
  if (typeof o.error === "string" && o.error.length > 0) {
    const redacted = redactSecretsFromLogSnippet(o.error);
    const snippet = redacted.slice(0, ERROR_DETAIL_MAX);
    details.push(`error=${snippet}${redacted.length > ERROR_DETAIL_MAX ? "…" : ""}`);
  }
  if (typeof o.cause === "string" && o.cause.length > 0) {
    const redacted = redactSecretsFromLogSnippet(o.cause);
    const snippet = redacted.slice(0, ERROR_DETAIL_MAX);
    details.push(`cause=${snippet}${redacted.length > ERROR_DETAIL_MAX ? "…" : ""}`);
  }

  if (details.length > 0) line += ` | ${details.join(" | ")}`;
  return line;
}

function truncate(line: string): string {
  if (line.length <= MAX_LINE_LENGTH) return line;
  return `${line.slice(0, MAX_LINE_LENGTH)}…`;
}
