/**
 * Helpers that shape provisioning progress payloads before they are emitted
 * to the renderer over IPC.
 *
 * Rationale: the renderer only renders a small "tail" preview of CLI logs
 * and assistant output in ProvisioningProgressBlock / CliLogsRichView. Sending
 * the full accumulated history on every throttled progress tick (≈ every
 * second under load) serialized a multi-megabyte string over IPC and forced
 * Zustand to produce a new immutable state object — which triggered renderer
 * V8 OOM crashes for users with long-running teams. These helpers keep the
 * hot emission path bounded while leaving the full history in-process for
 * diagnostics and completion-time reports.
 */

import type { TeamLaunchDiagnosticItem } from '@shared/types';

export const PROGRESS_LOG_TAIL_LINES = 200;
export const PROGRESS_OUTPUT_TAIL_PARTS = 20;
export const PROGRESS_TRACE_TAIL_LINES = 120;
export const PROGRESS_LAUNCH_DIAGNOSTICS_LIMIT = 20;
const PROGRESS_LAUNCH_DIAGNOSTIC_TEXT_LIMIT = 500;
const PROGRESS_TRACE_TEXT_LIMIT = 800;
const PROVIDER_API_KEY_FLAG_PATTERN =
  /(--(?:openai|codex|anthropic)[-_]api[-_]key(?:=|\s+))("[^"]*"|'[^']*'|\S+)/gi;
const SECRET_FLAG_PATTERN =
  /(--(?:api[-_]key|token|password|secret|authorization|auth[-_]token)(?:=|\s+))("[^"]*"|'[^']*'|\S+)/gi;
const SECRET_ENV_ASSIGNMENT_PATTERN =
  /\b([A-Z0-9_]*(?:API_KEY|TOKEN|SECRET|PASSWORD|AUTHORIZATION)[A-Z0-9_]*\s*=\s*)("[^"]*"|'[^']*'|\S+)/gi;
const AUTH_HEADER_PATTERN = /\b(Authorization\s*:\s*)(Bearer\s+)?("[^"]*"|'[^']*'|\S+)/gi;

/**
 * Return the trailing `maxLines` of a line-buffered CLI log, joined with "\n"
 * and trimmed. Returns `undefined` when the tail is empty so callers can
 * skip emitting a noop update.
 */
export function buildProgressLogsTail(
  lines: readonly string[],
  maxLines: number = PROGRESS_LOG_TAIL_LINES
): string | undefined {
  if (lines.length === 0) {
    return undefined;
  }
  const effectiveMax = Math.max(1, maxLines);
  const tail = lines.length > effectiveMax ? lines.slice(-effectiveMax) : lines;
  const joined = tail.join('\n').trim();
  return joined.length === 0 ? undefined : joined;
}

/**
 * Return the trailing `maxParts` of assistant output parts joined with a
 * blank line, matching the renderer's rendering contract. Returns `undefined`
 * when no parts are available.
 */
export function buildProgressAssistantOutput(
  parts: readonly string[],
  maxParts: number = PROGRESS_OUTPUT_TAIL_PARTS
): string | undefined {
  if (parts.length === 0) {
    return undefined;
  }
  const effectiveMax = Math.max(1, maxParts);
  const tail = parts.length > effectiveMax ? parts.slice(-effectiveMax) : parts;
  const joined = tail.join('\n\n');
  return joined.trim().length === 0 ? undefined : joined;
}

function boundRedactedText(
  value: string | undefined,
  limit: number,
  whitespace: 'collapse' | 'preserve'
): string | undefined {
  const prepared = whitespace === 'collapse' ? value?.replace(/\s+/g, ' ').trim() : value?.trim();
  if (!prepared) {
    return undefined;
  }
  const redacted = prepared
    .replace(PROVIDER_API_KEY_FLAG_PATTERN, '$1[redacted]')
    .replace(SECRET_FLAG_PATTERN, '$1[redacted]')
    .replace(SECRET_ENV_ASSIGNMENT_PATTERN, '$1[redacted]')
    .replace(AUTH_HEADER_PATTERN, '$1$2[redacted]')
    .replace(/```/g, "'''");
  return redacted.length > limit ? `${redacted.slice(0, limit - 3).trimEnd()}...` : redacted;
}

function boundDiagnosticText(value: string | undefined): string | undefined {
  return boundRedactedText(value, PROGRESS_LAUNCH_DIAGNOSTIC_TEXT_LIMIT, 'collapse');
}

export function buildProgressTraceLine(input: {
  timestamp: string;
  state: string;
  message: string;
  detail?: string;
}): string {
  const message = boundRedactedText(input.message, PROGRESS_TRACE_TEXT_LIMIT, 'collapse') ?? '';
  const detail = boundRedactedText(input.detail, PROGRESS_TRACE_TEXT_LIMIT, 'collapse');
  return detail
    ? `${input.timestamp} [${input.state}] ${message} - ${detail}`
    : `${input.timestamp} [${input.state}] ${message}`;
}

export function buildProgressTraceTail(
  lines: readonly string[],
  maxLines: number = PROGRESS_TRACE_TAIL_LINES
): string | undefined {
  return buildProgressLogsTail(lines, maxLines);
}

export function buildProgressLiveOutput(
  traceLines: readonly string[],
  assistantParts: readonly string[],
  options?: {
    maxTraceLines?: number;
    maxAssistantParts?: number;
  }
): string | undefined {
  const trace = buildProgressTraceTail(traceLines, options?.maxTraceLines);
  const assistant = buildProgressAssistantOutput(assistantParts, options?.maxAssistantParts);
  if (!trace) {
    return assistant;
  }
  const traceBlock = `**Launch trace**\n\n\`\`\`text\n${trace}\n\`\`\``;
  if (!assistant) {
    return traceBlock;
  }
  return `${traceBlock}\n\n**Runtime output**\n\n${assistant}`;
}

export function boundLaunchDiagnostics(
  items: readonly TeamLaunchDiagnosticItem[] | undefined,
  maxItems: number = PROGRESS_LAUNCH_DIAGNOSTICS_LIMIT
): TeamLaunchDiagnosticItem[] | undefined {
  if (!items || items.length === 0) {
    return undefined;
  }

  const bounded = items.slice(0, Math.max(1, maxItems)).map((item) => ({
    ...item,
    label: boundDiagnosticText(item.label) ?? item.code,
    detail: boundDiagnosticText(item.detail),
  }));
  return bounded.length > 0 ? bounded : undefined;
}
