import { randomBytes } from "node:crypto";
import { writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { DEFAULT_MAX_BYTES, formatSize, truncateTail } from "@mariozechner/pi-coding-agent";

function getTempFilePath() {
  const id = randomBytes(8).toString("hex");
  return join(tmpdir(), `pi-bash-${id}.log`);
}

function maybePersistFullOutput(fullOutput: string) {
  if (Buffer.byteLength(fullOutput, "utf-8") <= DEFAULT_MAX_BYTES) return undefined;
  const tempFilePath = getTempFilePath();
  writeFileSync(tempFilePath, fullOutput, "utf-8");
  return tempFilePath;
}

function buildTruncationNotice(
  truncation: ReturnType<typeof truncateTail>,
  fullOutput: string,
  tempFilePath?: string
) {
  const startLine = truncation.totalLines - truncation.outputLines + 1;
  const endLine = truncation.totalLines;

  if (truncation.lastLinePartial) {
    const lastLineSize = formatSize(Buffer.byteLength(fullOutput.split("\n").pop() || "", "utf-8"));
    return `\n\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;
  }
  if (truncation.truncatedBy === "lines") {
    return `\n\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${tempFilePath}]`;
  }
  return `\n\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;
}

export function buildSuccessfulBashResult(fullOutput: string) {
  const truncation = truncateTail(fullOutput);
  let outputText = truncation.content || "(no output)";
  const tempFilePath = truncation.truncated ? maybePersistFullOutput(fullOutput) : undefined;
  let details;

  if (truncation.truncated) {
    details = {
      truncation,
      fullOutputPath: tempFilePath,
    };
    outputText += buildTruncationNotice(truncation, fullOutput, tempFilePath);
  }

  return {
    content: [{ type: "text" as const, text: outputText }],
    details,
  };
}

export function buildExitCodeError(fullOutput: string, exitCode: number) {
  const result = buildSuccessfulBashResult(fullOutput);
  const outputText = result.content[0]?.text || "(no output)";
  return new Error(`${outputText}\n\nCommand exited with code ${exitCode}`);
}

export function buildAbortError(fullOutput: string) {
  const output = fullOutput === "(no output)" ? "" : fullOutput;
  return new Error(output ? `${output}\n\nCommand aborted` : "Command aborted");
}

export function buildTimeoutError(fullOutput: string, timeout: number) {
  const output = fullOutput === "(no output)" ? "" : fullOutput;
  return new Error(
    output
      ? `${output}\n\nCommand timed out after ${timeout} seconds`
      : `Command timed out after ${timeout} seconds`
  );
}
