import { Type } from '@sinclair/typebox';
import { AgentToolWithMeta, textResult } from '../common.js';
import { registerSchema } from '../catalog.js';
import { computeFirstChangedLine, createLineNumberedDiff } from '../edit_diff.js';
import { withFileMutationQueueOps } from '../file_mutation_queue.js';
import { fuzzyFindAll } from '../fuzzy_match.js';
import { resolveToolPathFromParams } from '../path_utils.js';
import { getDefaultOperations, type ToolOperations } from '../operations/index.js';

const PatchParams = Type.Object({
  path: Type.String({ description: '要修改的文件路径' }),
  old_string: Type.String({ description: '要查找的文本片段' }),
  new_string: Type.String({ description: '替换后的文本片段' }),
  replace_all: Type.Optional(Type.Boolean({ description: '是否替换全部匹配项，默认 false' })),
});

export function createPatchTool(opts?: { operations?: ToolOperations }): AgentToolWithMeta<typeof PatchParams, unknown> {
  const ops = opts?.operations ?? getDefaultOperations();
  return {
    name: 'patch',
    label: '模糊补丁',
    description: '对文件执行局部查找替换，支持模糊匹配；会结合会话工作目录解析相对路径，并对同一文件串行化写入。',
    parameters: PatchParams,
    async execute(_id, params) {
      try {
        const targetPath = resolveToolPathFromParams(params.path, params as Record<string, unknown>);
        return await withFileMutationQueueOps(targetPath, ops.file, async () => {
          const buffer = await ops.file.readFile(targetPath);
          const content = buffer.toString('utf-8');
          const matches = fuzzyFindAll(content, params.old_string);

          if (matches.length === 0) {
            return textResult('未找到匹配内容，请检查 old_string 是否准确，或提供更多上下文。');
          }

          if (matches.length > 1 && !params.replace_all) {
            return textResult(
              `找到 ${matches.length} 处匹配（策略: ${matches[0].strategy}），请补充更多上下文以确保唯一匹配，或设置 replace_all=true。`
            );
          }

          let result = content;
          const sorted = [...matches].sort((a, b) => b.startIndex - a.startIndex);
          for (const match of sorted) {
            result = result.slice(0, match.startIndex) + params.new_string + result.slice(match.endIndex);
          }

          await ops.file.writeFile(targetPath, result);

          const count = params.replace_all ? matches.length : 1;
          const firstChangedLine = computeFirstChangedLine(content, result);
          const diff = createLineNumberedDiff(content, result);
          const output: string[] = [
            `patch 已完成：${targetPath}`,
            `替换数量：${count}`,
            `匹配策略：${matches[0].strategy}`,
          ];

          if (firstChangedLine) {
            output.push(`首个变更行：${firstChangedLine}`);
          }

          if (diff) {
            output.push('', '[patch 差异预览]', diff);
          }

          return textResult(output.join('\n'));
        });
      } catch (err: any) {
        return textResult(`patch 失败: ${err?.message || String(err)}`);
      }
    },
  };
}

export const patchTool = createPatchTool();

registerSchema({
  name: 'patch',
  toolset: 'base',
  description: patchTool.description,
  capability: 'text',
  visibility: 'tool',
  supportsPromptHint: true,
});
