// src/engine/reflection/activity-summarizer.ts
export interface ActivityEvent {
  type: string;          // 'tool_call' | 'tool_result' | 'tool_error' | 'transition' | 'system' | ...
  tool?: string;
  args?: unknown;
  error?: string;
  result?: unknown;
  from?: string;         // for transitions
  to?: string;
  reason?: string;
  ts?: string;
}

const KEEP_ALWAYS = new Set(['tool_error', 'transition', 'system_warning', 'system_error']);

function fmt(ev: ActivityEvent): string {
  switch (ev.type) {
    case 'tool_error':
      return `! ${ev.tool ?? '?'}: ${ev.error ?? ''}`;
    case 'transition':
      return `→ ${ev.from} -> ${ev.to}${ev.reason ? ` (${ev.reason})` : ''}`;
    case 'tool_call':
      // For complete() include result; for others just the name
      if (ev.tool === 'complete' || ev.tool === 'transition') {
        const a = ev.args ? JSON.stringify(ev.args).slice(0, 240) : '';
        return `· ${ev.tool}${a ? ` ${a}` : ''}`;
      }
      return `· ${ev.tool ?? '?'}`;
    case 'system_warning':
    case 'system_error':
      return `! ${ev.type}: ${ev.reason ?? ''}`;
    default:
      return `· ${ev.type}`;
  }
}

export function summarizeActivityLog(events: ActivityEvent[], maxBytes: number): string {
  // Pass 1: keep important events; collapse runs of identical tool_call lines.
  const lines: string[] = [];
  let prev: string | null = null;
  let runCount = 0;
  for (const ev of events) {
    const keep = KEEP_ALWAYS.has(ev.type) || ev.tool === 'complete' || ev.tool === 'transition' || ev.type === 'tool_call';
    if (!keep) continue;
    const line = fmt(ev);
    if (line === prev) {
      runCount++;
      continue;
    }
    if (runCount > 0 && prev) {
      lines[lines.length - 1] = `${prev}  ×${runCount + 1}`;
    }
    lines.push(line);
    prev = line;
    runCount = 0;
  }
  if (runCount > 0 && prev) {
    lines[lines.length - 1] = `${prev}  ×${runCount + 1}`;
  }

  // Pass 2: greedily fit into maxBytes, prioritizing errors + complete() at the tail.
  // Strategy: always include the tail (last 25%); fill the rest from the head.
  let out = lines.join('\n');
  if (Buffer.byteLength(out, 'utf8') <= maxBytes) return out;

  const tailBudget = Math.floor(maxBytes * 0.4);
  const headBudget = maxBytes - tailBudget - 16; // reserve for "...truncated..."
  let head = '';
  let tail = '';
  for (const l of lines) {
    if (Buffer.byteLength(head + l + '\n', 'utf8') > headBudget) break;
    head += l + '\n';
  }
  for (let i = lines.length - 1; i >= 0; i--) {
    if (Buffer.byteLength(lines[i] + '\n' + tail, 'utf8') > tailBudget) break;
    tail = lines[i] + '\n' + tail;
  }
  return `${head}\n...truncated...\n${tail}`.slice(0, maxBytes);
}
