/**
 * MultiEdit tool — apply multiple edits in a single operation.
 */

import { readFileSync, writeFileSync, existsSync } from 'fs';
import { resolve, normalize } from 'path';

export const definition = {
  name: 'MultiEdit',
  description: 'Perform multiple edits to files in a single operation. Each edit is an exact string replacement applied sequentially.',
  input_schema: {
    type: 'object',
    properties: {
      edits: {
        type: 'array',
        description: 'Array of edits to apply',
        items: {
          type: 'object',
          properties: {
            file_path: { type: 'string', description: 'Path to the file' },
            old_string: { type: 'string', description: 'Text to replace' },
            new_string: { type: 'string', description: 'Replacement text' }
          },
          required: ['file_path', 'old_string', 'new_string']
        }
      }
    },
    required: ['edits']
  }
};

export async function execute(input, context) {
  const { edits } = input;

  if (!Array.isArray(edits) || edits.length === 0) {
    return { content: 'Error: edits array is required and must not be empty', is_error: true };
  }

  const wsNorm = resolve(normalize(context.workspaceRoot));
  const results = [];
  // Group edits by file to apply sequentially
  const fileContents = new Map();

  for (let i = 0; i < edits.length; i++) {
    const edit = edits[i];
    const { file_path, old_string, new_string } = edit;

    if (!file_path || old_string === undefined || new_string === undefined) {
      results.push(`Edit ${i + 1}: Error — missing required fields`);
      continue;
    }

    const resolved = resolve(normalize(file_path));

    if (!resolved.startsWith(wsNorm)) {
      results.push(`Edit ${i + 1}: Error — path outside workspace`);
      continue;
    }

    if (!existsSync(resolved) && !fileContents.has(resolved)) {
      results.push(`Edit ${i + 1}: Error — file not found: ${resolved}`);
      continue;
    }

    // Safety: must have been Read first
    if (context.readFileTimestamps && !context.readFileTimestamps[resolved]) {
      results.push(`Edit ${i + 1}: Error — file not Read in this session`);
      continue;
    }

    // Get current content (from cache if already modified in this batch)
    let content;
    if (fileContents.has(resolved)) {
      content = fileContents.get(resolved);
    } else {
      content = readFileSync(resolved, 'utf-8');
    }

    const idx = content.indexOf(old_string);
    if (idx === -1) {
      results.push(`Edit ${i + 1}: Error — old_string not found in ${resolved}`);
      continue;
    }

    const updated = content.slice(0, idx) + new_string + content.slice(idx + old_string.length);
    fileContents.set(resolved, updated);
    results.push(`Edit ${i + 1}: OK — replaced in ${resolved}`);
  }

  // Write all modified files
  let writtenCount = 0;
  for (const [filePath, content] of fileContents) {
    try {
      writeFileSync(filePath, content, 'utf-8');
      writtenCount++;
    } catch (err) {
      results.push(`Write error for ${filePath}: ${err.message}`);
    }
  }

  return {
    content: `MultiEdit: ${writtenCount} file(s) written.\n${results.join('\n')}`,
    is_error: false
  };
}
