// Qa Lab plugin module implements child output behavior.
export const QA_CHILD_STDOUT_MAX_BYTES = 1024 * 1024;
export const QA_CHILD_STDERR_TAIL_BYTES = 64 * 1024;

export type QaChildOutputCapture = {
  chunks: Buffer[];
  bytes: number;
  exceeded: boolean;
  maxBytes: number;
};

export type QaChildOutputTail = {
  buffer: Buffer;
  maxBytes: number;
  truncated: boolean;
};

function toBuffer(chunk: unknown): Buffer {
  return Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
}

export function createQaChildOutputCapture(maxBytes = QA_CHILD_STDOUT_MAX_BYTES) {
  return {
    chunks: [],
    bytes: 0,
    exceeded: false,
    maxBytes,
  } satisfies QaChildOutputCapture;
}

export function appendQaChildOutput(capture: QaChildOutputCapture, chunk: unknown) {
  if (capture.exceeded) {
    return;
  }
  const buffer = toBuffer(chunk);
  const remainingBytes = capture.maxBytes - capture.bytes;
  if (buffer.byteLength > remainingBytes) {
    if (remainingBytes > 0) {
      capture.chunks.push(Buffer.from(buffer.subarray(0, remainingBytes)));
    }
    capture.bytes = capture.maxBytes;
    capture.exceeded = true;
    return;
  }
  capture.chunks.push(Buffer.from(buffer));
  capture.bytes += buffer.byteLength;
}

export function readQaChildOutput(capture: QaChildOutputCapture) {
  return Buffer.concat(capture.chunks, capture.bytes).toString("utf8");
}

export function createQaChildOutputTail(maxBytes = QA_CHILD_STDERR_TAIL_BYTES) {
  return {
    buffer: Buffer.alloc(0),
    maxBytes,
    truncated: false,
  } satisfies QaChildOutputTail;
}

export function appendQaChildOutputTail(tail: QaChildOutputTail, chunk: unknown) {
  const buffer = toBuffer(chunk);
  if (buffer.byteLength >= tail.maxBytes) {
    tail.buffer = Buffer.from(buffer.subarray(buffer.byteLength - tail.maxBytes));
    tail.truncated = true;
    return;
  }
  const next = Buffer.concat([tail.buffer, buffer], tail.buffer.byteLength + buffer.byteLength);
  if (next.byteLength <= tail.maxBytes) {
    tail.buffer = next;
    return;
  }
  tail.buffer = Buffer.from(next.subarray(next.byteLength - tail.maxBytes));
  tail.truncated = true;
}

export function formatQaChildOutputTail(tail: QaChildOutputTail, label: string) {
  const text = tail.buffer.toString("utf8").trim();
  if (!text) {
    return "";
  }
  return tail.truncated ? `[${label} truncated to last ${tail.maxBytes} bytes]\n${text}` : text;
}
