import { z } from 'zod';
import type { ToolDef, ToolContext } from './types.js';
import { queueEdit } from '../pending_changes.js';
import { denyReadOnly, finalizeWrite } from './write-gate.js';

const EditFileInput = z.object({
  file_path: z.string().min(1),
  old_string: z.string().min(1),
  new_string: z.string(),
});
type EditFileInputT = z.infer<typeof EditFileInput>;

export const editFileTool: ToolDef<EditFileInputT> = {
  name: 'edit_file',
  description:
    'Queue an edit to a file. The edit replaces old_string with new_string. ' +
    'The change is staged in pending_changes and must be applied explicitly.',
  inputSchema: EditFileInput,
  jsonSchema: {
    type: 'function',
    function: {
      name: 'edit_file',
      description:
        'Queue an edit to a file. The edit replaces old_string with new_string. ' +
        'The change is staged in pending_changes and must be applied explicitly.',
      parameters: {
        type: 'object',
        properties: {
          file_path: { type: 'string', description: 'Path to the file to edit (relative to project root or absolute)' },
          old_string: { type: 'string', description: 'The exact string to find and replace (must appear in the file)' },
          new_string: { type: 'string', description: 'The replacement string' },
        },
        required: ['file_path', 'old_string', 'new_string'],
      },
    },
  },
  async execute(input: EditFileInputT, projectRoot: string, context: ToolContext): Promise<unknown> {
    if (context.permissionMode === 'plan') return denyReadOnly('edit_file');
    const change = await queueEdit(
      context.sql,
      context.sessionId,
      context.taskId,
      input.file_path,
      input.old_string,
      input.new_string,
      projectRoot,
    );
    return finalizeWrite(
      context,
      projectRoot,
      change,
      `Edit queued for ${change.file_path}. Use apply_pending to write changes to disk.`,
    );
  },
};
