/**
 * Compact unified-diff generator for write-tool results.
 *
 * Produces a minimal unified diff string (---/+++ header + +/- lines) from
 * old/new text pairs so the frontend can render an inline diff snippet
 * without pulling in a full diff library.
 */

export const WRITE_TOOL_NAMES = new Set([
  'edit_file',
  'create_file',
  'delete_file',
  'apply_pending',
]);

/**
 * Compute a compact unified diff from old → new text.
 *
 * @param oldStr  The original text (empty for creates)
 * @param newStr  The replacement text (empty for deletes)
 * @param filePath  Display path for the file header
 * @returns A unified-diff string, or empty string if old === new
 */
export function computeDiff(oldStr: string, newStr: string, filePath: string): string {
  if (oldStr === newStr) return '';

  const oldLines = oldStr.split('\n');
  const newLines = newStr.split('\n');

  // For empty old → new file (create), show all lines as additions
  if (oldStr.length === 0 && newStr.length > 0) {
    const header = `--- /dev/null\n+++ b/${filePath}\n`;
    const body = newLines.map((line) => `+${line}`).join('\n');
    return header + body;
  }

  // For old → empty (delete), show all lines as removals
  if (newStr.length === 0 && oldStr.length > 0) {
    const header = `--- a/${filePath}\n+++ /dev/null\n`;
    const body = oldLines.map((line) => `-${line}`).join('\n');
    return header + body;
  }

  // Simple line-by-line diff for edit: collect changed lines with context.
  // Uses a straightforward algorithm: find the first differing line and the
  // last differing line, then output the block with +/- markers.
  const header = `--- a/${filePath}\n+++ b/${filePath}\n`;

  const maxLen = Math.max(oldLines.length, newLines.length);
  let firstDiff = -1;
  let lastDiff = -1;

  for (let i = 0; i < maxLen; i++) {
    const a = i < oldLines.length ? oldLines[i] : undefined;
    const b = i < newLines.length ? newLines[i] : undefined;
    if (a !== b) {
      if (firstDiff === -1) firstDiff = i;
      lastDiff = i;
    }
  }

  if (firstDiff === -1) return '';

  // Add context lines around the changed block (up to 2 lines each side)
  const contextBefore = 2;
  const contextAfter = 2;
  const start = Math.max(0, firstDiff - contextBefore);
  const end = Math.min(maxLen - 1, lastDiff + contextAfter);

  const hunkLines: string[] = [];
  const hunkOldStart = start + 1; // 1-indexed
  const hunkNewStart = start + 1;
  const hunkOldLen = end - start + 1;
  const hunkNewLen = end - start + 1;

  for (let i = start; i <= end; i++) {
    const oldLine = i < oldLines.length ? oldLines[i] : undefined;
    const newLine = i < newLines.length ? newLines[i] : undefined;

    if (oldLine === newLine) {
      hunkLines.push(` ${oldLine ?? ''}`);
    } else {
      if (oldLine !== undefined) {
        hunkLines.push(`-${oldLine}`);
      }
      if (newLine !== undefined) {
        hunkLines.push(`+${newLine}`);
      }
    }
  }

  const hunkHeader = `@@ -${hunkOldStart},${hunkOldLen} +${hunkNewStart},${hunkNewLen} @@\n`;
  return header + hunkHeader + hunkLines.join('\n');
}

/**
 * Check whether a tool name corresponds to a file-modifying write tool
 * that should produce a diff in its tool result.
 */
export function isWriteTool(name: string): boolean {
  return WRITE_TOOL_NAMES.has(name);
}

/**
 * Extract a diff string from tool call args for write tools.
 * Returns empty string if the tool doesn't produce diffs or args are missing.
 */
export function diffFromToolArgs(name: string, args: Record<string, unknown>, filePath?: string): string {
  switch (name) {
    case 'edit_file': {
      const oldStr = String(args.old_string ?? '');
      const newStr = String(args.new_string ?? '');
      const path = filePath ?? String(args.file_path ?? 'file');
      return computeDiff(oldStr, newStr, path);
    }
    case 'create_file': {
      const content = String(args.content ?? '');
      const path = filePath ?? String(args.file_path ?? 'file');
      return computeDiff('', content, path);
    }
    case 'delete_file':
      // No content available at queue time — actual content is read at apply time.
      return '';
    case 'apply_pending':
      // Meta-tool — individual changes produce their own diffs.
      return '';
    default:
      return '';
  }
}
