import type { Message, ToolCall } from '../../llm/openai-compat.js';

const READ_TOOL_NAMES = new Set(['Read']);
const PLACEHOLDER_PREFIX = '[Duplicate Read of';

export interface DedupResult {
  changed: boolean;
  replacedCount: number;
  freedChars: number;
}

function extractReadFilePath(toolCall: ToolCall): string | null {
  if (!READ_TOOL_NAMES.has(toolCall.function.name)) return null;
  try {
    const args = JSON.parse(toolCall.function.arguments) as { file_path?: unknown };
    if (typeof args.file_path === 'string' && args.file_path.length > 0) {
      return args.file_path;
    }
  } catch {
    // malformed JSON — skip this call
  }
  return null;
}

function buildPlaceholder(filePath: string): string {
  return `${PLACEHOLDER_PREFIX} ${filePath} — see the latest Read of this file later in the conversation. Use Read(offset/limit) for a narrower range if needed.]`;
}

/**
 * Replace older Read tool results of the same file with a short placeholder,
 * keeping only the most recent Read content. Mutates `messages` in place.
 *
 * Dedupes by file_path only (offset/limit ignored). The placeholder hint
 * nudges the agent to re-Read with a narrower range when needed, which is
 * cheaper than retaining stale full-file reads in context.
 */
export function dedupeFileReads(messages: Message[]): DedupResult {
  const callIdToPath = new Map<string, string>();
  for (const message of messages) {
    if (message.role !== 'assistant' || !message.tool_calls) continue;
    for (const toolCall of message.tool_calls) {
      const path = extractReadFilePath(toolCall);
      if (path) callIdToPath.set(toolCall.id, path);
    }
  }
  if (callIdToPath.size === 0) {
    return { changed: false, replacedCount: 0, freedChars: 0 };
  }

  const seenPaths = new Set<string>();
  let replacedCount = 0;
  let freedChars = 0;
  for (let i = messages.length - 1; i >= 0; i--) {
    const message = messages[i]!;
    if (message.role !== 'tool' || !message.tool_call_id) continue;
    const path = callIdToPath.get(message.tool_call_id);
    if (!path) continue;
    if (!seenPaths.has(path)) {
      seenPaths.add(path);
      continue; // keep the most recent Read content
    }
    const original = typeof message.content === 'string' ? message.content : '';
    if (original.startsWith(PLACEHOLDER_PREFIX)) continue; // already deduped
    const placeholder = buildPlaceholder(path);
    message.content = placeholder;
    replacedCount++;
    freedChars += Math.max(0, original.length - placeholder.length);
  }
  return { changed: replacedCount > 0, replacedCount, freedChars };
}
