/**
 * Tool Registry - 工具注册与调度中心
 * 管理所有可用工具的定义，负责执行调度
 */

import type { ToolDefinition, ToolResult } from '../types.js';
import { state, KEYS } from '../state/state.js';
import { logToolStart, logToolResult, logError, logInfo, logWarn } from './log-service.js';
import { getMCPToolDefinitions } from './mcp-client.js';

export const TOOL_DEFINITIONS: ToolDefinition[] = [
  {
    type: 'function',
    function: {
      name: 'read_file',
      description: 'Read a file from the local filesystem. Supports text mode (utf-8/latin1, line-based pagination) and binary mode (base64, byte-based pagination). Files up to 5MB (text) or 50MB (binary). When truncated, returns remaining_lines/remaining_bytes and a hint to continue reading. Use start_line/end_line for text files, offset_bytes/limit_bytes for binary files or raw byte access.',
      parameters: {
        type: 'object',
        required: ['path'],
        properties: {
          path: { type: 'string', description: 'The file path to read. Absolute or relative to workspace.' },
          encoding: { type: 'string', enum: ['utf-8', 'latin1', 'base64'], description: 'File encoding. Default: utf-8. Use base64 for binary content.' },
          start_line: { type: 'integer', description: 'Start line (1-indexed) for text files. Use with end_line for pagination.' },
          end_line: { type: 'integer', description: 'End line (inclusive). Default: start_line + 2000.' },
          mode: { type: 'string', enum: ['text', 'binary'], description: 'Read mode. text = decode as string, binary = return base64. Default: text.' },
          offset_bytes: { type: 'integer', description: 'Byte offset to start reading from (0-indexed). Use for binary files or large text.' },
          limit_bytes: { type: 'integer', description: 'Max bytes to read. Default: 50000 (text) or 102400 (binary).' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'write_file',
      description: 'Write content to a local file. Creates parent directories automatically. The "path" parameter is REQUIRED — always specify a file path (relative to workspace or absolute). For example: path="output.txt" writes to workspace. Supports text (utf-8/latin1) and binary (base64). Use mode="append" to add to existing file. Max 10MB overwrite / 5MB append.',
      parameters: {
        type: 'object',
        required: ['path', 'content'],
        properties: {
          path: { type: 'string', description: 'REQUIRED — File path. Relative paths use workspace as base. E.g. "output.txt", "docs/report.md", or absolute "/home/user/file.txt".' },
          content: { type: 'string', description: 'The content to write. For binary files, pass base64-encoded string with encoding="base64".' },
          encoding: { type: 'string', enum: ['utf-8', 'latin1', 'base64'], description: 'File encoding. Use "base64" to write binary content (images, PDFs, etc). Default: utf-8.' },
          mode: { type: 'string', enum: ['overwrite', 'append'], description: 'Write mode. overwrite = replace file, append = add to end. Default: overwrite.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'list_directory',
      description: 'List directory contents up to 2000 entries. Returns names, types, sizes, modification times. Supports offset pagination, recursion, and extension filtering.',
      parameters: {
        type: 'object',
        required: ['path'],
        properties: {
          path: { type: 'string', description: 'Directory path.' },
          recursive: { type: 'boolean', description: 'List recursively. Default: false.' },
          max_depth: { type: 'integer', description: 'Max recursion depth. Default: 3.' },
          include_hidden: { type: 'boolean', description: 'Include hidden files. Default: false.' },
          filter_extension: { type: 'string', description: 'Filter files by extension (e.g. ".txt"). Only files matching this extension are returned. Default: no filter.' },
          offset: { type: 'integer', description: 'Skip first N entries for pagination. Default: 0.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'search_files',
      description: 'Search files by name pattern or text content within files. Supports regular expressions with use_regex=true.',
      parameters: {
        type: 'object',
        required: ['path', 'query'],
        properties: {
          path: { type: 'string', description: 'Root directory to search.' },
          query: { type: 'string', description: 'Search query (glob, text, or regex).' },
          search_type: { type: 'string', enum: ['filename', 'content', 'both'], description: 'Search target. Default: both.' },
          case_sensitive: { type: 'boolean', description: 'Case sensitive. Default: false.' },
          max_results: { type: 'integer', description: 'Max results. Default: 0 (no limit).' },
          file_extensions: { type: 'array', items: { type: 'string' }, description: 'Filter extensions, e.g. [".ts", ".js"]' },
          use_regex: { type: 'boolean', description: 'Treat query as regular expression. Default: false.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'create_directory',
      description: 'Create a new directory. Creates parents automatically.',
      parameters: {
        type: 'object',
        required: ['path'],
        properties: {
          path: { type: 'string', description: 'Directory path to create.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'delete_file',
      description: 'Delete a file or directory. Supports batch deletion via paths parameter. At least one of path or paths must be provided. Returns deleted item count and total size. Use recursive=true for non-empty directories.',
      parameters: {
        type: 'object',
        properties: {
          path: { type: 'string', description: 'Single path to delete. Use this for deleting one file/directory.' },
          paths: { type: 'array', items: { type: 'string' }, description: 'Multiple paths to delete in batch. Use this when deleting multiple files at once.' },
          recursive: { type: 'boolean', description: 'Recursive delete for directories. Returns filesDeleted count and total deletedSize.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'run_command',
      description: 'Execute a shell command via workspace. Timeout configurable (default 300s for general, 600s for long-running). cwd defaults to workspace directory. Requires user confirmation. Output is streamed in real-time through the workspace process.',
      parameters: {
        type: 'object',
        required: ['command'],
        properties: {
          command: { type: 'string', description: 'Shell command to execute.' },
          cwd: { type: 'string', description: 'Working directory. Defaults to workspace directory.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'move_file',
      description: 'Move or rename a file/directory. Requires user confirmation.',
      parameters: {
        type: 'object',
        required: ['source', 'destination'],
        properties: {
          source: { type: 'string', description: 'Source path.' },
          destination: { type: 'string', description: 'Destination path.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'copy_file',
      description: 'Copy a file or directory to a new location. Requires user confirmation.',
      parameters: {
        type: 'object',
        required: ['source', 'destination'],
        properties: {
          source: { type: 'string', description: 'Source path.' },
          destination: { type: 'string', description: 'Destination path.' },
          recursive: { type: 'boolean', description: 'Copy directories recursively. Default: true.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'web_fetch',
      description: 'Fetch and extract full text content from a URL. Typically used after web_search: pick the most relevant result URLs from search and call web_fetch on each to get detailed content before answering. Automatically retries on failure (up to 2 times with exponential backoff). Auto-upgrades to browser rendering for JavaScript-heavy pages.',
      parameters: {
        type: 'object',
        required: ['url'],
        properties: {
          url: { type: 'string', description: 'URL to fetch (http/https).' },
          max_chars: { type: 'integer', description: 'Max characters to return. Default: 0 (no limit, return full content).' },
          extract_mode: { type: 'string', enum: ['text', 'markdown'], description: 'Extraction mode. Default: text.' },
          mobile_ua: { type: 'boolean', description: 'Use mobile User-Agent. Some sites return simpler content for mobile. Default: false.' },
          retry: { type: 'boolean', description: 'Enable auto-retry on failure (5xx/network errors, up to 2 times). Default: true.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'edit_file',
      description: 'Find and replace text in a file. Supports literal string matching and regex (use_regex=true). Use all=true to replace all occurrences. More efficient than write_file for small edits.',
      parameters: {
        type: 'object',
        required: ['path', 'old_text', 'new_text'],
        properties: {
          path: { type: 'string', description: 'File path to edit.' },
          old_text: { type: 'string', description: 'Text to find. Can be a regex pattern when use_regex=true.' },
          new_text: { type: 'string', description: 'Replacement text.' },
          all: { type: 'boolean', description: 'Replace all occurrences. Default: false.' },
          use_regex: { type: 'boolean', description: 'Treat old_text as a regular expression. Default: false.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'get_file_info',
      description: 'Get detailed file or directory information: size, dates, permissions, type.',
      parameters: {
        type: 'object',
        required: ['path'],
        properties: {
          path: { type: 'string', description: 'File or directory path.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'tree',
      description: 'Display directory structure as a tree. Shows nested files and folders. Default depth: 5 levels.',
      parameters: {
        type: 'object',
        required: ['path'],
        properties: {
          path: { type: 'string', description: 'Root directory path.' },
          max_depth: { type: 'integer', description: 'Max depth. Default: 5.' },
          include_hidden: { type: 'boolean', description: 'Include hidden files. Default: false.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'download_file',
      description: 'Download a file from a URL to a local path. Requires user confirmation.',
      parameters: {
        type: 'object',
        required: ['url', 'destination'],
        properties: {
          url: { type: 'string', description: 'URL to download (http/https).' },
          destination: { type: 'string', description: 'Local path to save the file.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'diff_files',
      description: 'Compare two files and show differences. Returns a unified diff.',
      parameters: {
        type: 'object',
        required: ['file1', 'file2'],
        properties: {
          file1: { type: 'string', description: 'First file path.' },
          file2: { type: 'string', description: 'Second file path.' },
          context_lines: { type: 'integer', description: 'Context lines around changes. Default: 3.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'replace_in_files',
      description: 'Find and replace text across multiple files matching a glob pattern. Requires user confirmation.',
      parameters: {
        type: 'object',
        required: ['path', 'glob', 'old_text', 'new_text'],
        properties: {
          path: { type: 'string', description: 'Root directory.' },
          glob: { type: 'string', description: 'File pattern, e.g. "*.ts", "**/*.js".' },
          old_text: { type: 'string', description: 'Text to find.' },
          new_text: { type: 'string', description: 'Replacement text.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'read_multiple_files',
      description: 'Read up to 50 files at once in parallel. Returns content of all requested files (10KB each by default).',
      parameters: {
        type: 'object',
        required: ['paths'],
        properties: {
          paths: { type: 'array', items: { type: 'string' }, description: 'Array of file paths to read.' },
          max_chars_per_file: { type: 'integer', description: 'Max chars per file. Default: 10000.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'git',
      description: 'Git operations: status, log, diff, add, commit, push, pull, branch, checkout, merge, stash, remote, clone, init. All operations are automatic (no confirmation).',
      parameters: {
        type: 'object',
        required: ['action'],
        properties: {
          action: { type: 'string', enum: ['status', 'log', 'diff', 'add', 'commit', 'push', 'pull', 'branch', 'checkout', 'merge', 'stash', 'remote', 'clone', 'init', 'reset', 'tag'], description: 'Git action to perform.' },
          path: { type: 'string', description: 'Repository path. Defaults to workspace.' },
          files: { type: 'array', items: { type: 'string' }, description: 'Files for add/diff/checkout.' },
          message: { type: 'string', description: 'Commit message for commit action.' },
          branch: { type: 'string', description: 'Branch name for branch/checkout/merge.' },
          tag_name: { type: 'string', description: 'Tag name for tag action (creates an annotated tag if message is provided).' },
          stash_sub: { type: 'string', enum: ['push', 'pop', 'apply', 'list', 'drop'], description: 'Stash subcommand. Default: push.' },
          remote: { type: 'string', description: 'Remote name for push/pull/remote.' },
          remote_url: { type: 'string', description: 'Remote URL for remote add.' },
          count: { type: 'integer', description: 'Number of log entries. Default: 20.' },
          all: { type: 'boolean', description: 'Show all (branches, stashes, etc). Default: false.' },
          staged: { type: 'boolean', description: 'Show staged changes in diff. Default: false.' },
          new_branch: { type: 'boolean', description: 'Create new branch in branch action. Default: false.' },
          delete_branch: { type: 'boolean', description: 'Delete branch in branch action. Default: false.' },
          force: { type: 'boolean', description: 'Force push/checkout. Default: false.' },
          url: { type: 'string', description: 'Repository URL for clone.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'compress',
      description: 'Create or extract archives (zip/tar.gz). Requires user confirmation for create.',
      parameters: {
        type: 'object',
        required: ['action', 'path'],
        properties: {
          action: { type: 'string', enum: ['create', 'extract'], description: 'Create or extract archive.' },
          path: { type: 'string', description: 'Source path (create) or archive path (extract).' },
          destination: { type: 'string', description: 'Output archive path (create) or extract dir (extract).' },
          format: { type: 'string', enum: ['zip', 'tar.gz'], description: 'Archive format. Default: tar.gz.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'web_search',
      description: 'Search the web with auto-fetch. Always use this when the user asks for information, news, facts, or current data. Never rely on training data or past conversation results — call this tool every time the user asks to search. Set fetch_top to auto-fetch full page content from the top N results.',
      parameters: {
        type: 'object',
        required: ['query', 'fetch_top'],
        properties: {
          query: { type: 'string', description: 'The search query. Be specific and concise for best results.' },
          max_results: { type: 'integer', description: 'Maximum number of results to return. Default: 30, max: 30.' },
          time_range: { type: 'string', enum: ['day', 'week', 'month', 'year'], description: 'Filter results by time. Supported by Bing and Google. Use for recent news, latest version, etc.' },
          enhance_snippets: { type: 'boolean', description: 'Auto-fetch detailed snippet for results with too-short descriptions. Default: true.' },
          fetch_top: { type: 'integer', description: 'Number of top results to auto-fetch full content for. Min: 3. Results shorter than this are useless — snippets lack detail.' }
        }
      }
    }
  },
  // ══════════════════════════════════════════════
  // 记忆工具（统一入口，读写工作空间 MEMORY.md）
  // ══════════════════════════════════════════════
  {
    type: 'function',
    function: {
      name: 'memory',
      description: `Manage agent memories stored in MEMORY.md. Usage by action:
- search: {"action":"search","query":"keywords","limit":8}
- add: {"action":"add","type":"fact","content":"the memory content","importance":8,"tags":["tag1","tag2"]}  ← type and content are REQUIRED for add!
- replace: {"action":"replace","old_text":"unique substring","new_content":"replacement"}
- remove: {"action":"remove","old_text":"unique substring"}
- remove_batch: {"action":"remove_batch","old_texts":["substring1","substring2","substring3"]}  ← Batch delete multiple entries in one call
- read_all: {"action":"read_all"}

CRITICAL: For add action, you MUST include both "type" (fact/preference/rule) and "content" fields. If you omit them, the call will fail.
TIP: Use remove_batch when deleting multiple entries — it's much more efficient than calling remove repeatedly.`,
      parameters: {
        type: 'object',
        required: ['action'],
        properties: {
          action: { type: 'string', enum: ['search', 'add', 'replace', 'remove', 'remove_batch', 'read_all'], description: 'Which operation to perform. Note: "add" requires "type"+"content", "replace" requires "old_text"+"new_content", "remove" requires "old_text", "remove_batch" requires "old_texts" (array), "search" requires "query".' },
          query: { type: 'string', description: '[search REQUIRED] Keywords to search for.' },
          limit: { type: 'integer', description: '[search] Max results. Default: 8.' },
          type: { type: 'string', enum: ['fact', 'preference', 'rule'], description: '[add REQUIRED] Memory type. fact=user info, preference=user preference, rule=behavior rule.' },
          content: { type: 'string', description: '[add REQUIRED, replace] Memory text. Keep it concise (8-100 chars).' },
          importance: { type: 'integer', description: '[add] Priority 1-10. Default: 5. Use 8+ for critical info.' },
          tags: { type: 'array', items: { type: 'string' }, description: '[add] 2-5 keywords for search matching.' },
          old_text: { type: 'string', description: '[replace, remove REQUIRED] Unique substring to identify the entry. Must be ≥5 chars. Use exact text from read_all for best results.' },
          old_texts: { type: 'array', items: { type: 'string' }, description: '[remove_batch REQUIRED] Array of substrings to identify multiple entries for batch deletion. Each must be ≥5 chars.' },
          new_content: { type: 'string', description: '[replace REQUIRED] New text to replace with.' }
        }
      }
    }
  },
  // ══════════════════════════════════════════════
  // 会话管理工具
  // ══════════════════════════════════════════════
  {
    type: 'function',
    function: {
      name: 'session_list',
      description: 'List previous chat sessions with their titles and timestamps. Useful for referencing past conversations.',
      parameters: {
        type: 'object',
        properties: {
          limit: { type: 'integer', description: 'Max sessions to return. Default: 20.' },
          search: { type: 'string', description: 'Filter sessions by title keyword.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'session_read',
      description: 'Read the messages from a previous chat session. Use when the user references something from a past conversation.',
      parameters: {
        type: 'object',
        required: ['session_id'],
        properties: {
          session_id: { type: 'string', description: 'The session ID to read.' },
          max_messages: { type: 'integer', description: 'Max messages to return. Default: 50.' }
        }
      }
    }
  },
  // ══════════════════════════════════════════════
  // v4.3 新增工具：子代理委派
  // ══════════════════════════════════════════════
  {
    type: 'function',
    function: {
      name: 'spawn_task',
      description: 'Spawn a sub-agent to independently execute a task using read-only tools (file reading, web search, browser viewing, memory/session queries). Use this to parallelize independent research or analysis sub-tasks. The model is configured in Settings and cannot be overridden per call.',
      parameters: {
        type: 'object',
        required: ['task'],
        properties: {
          task: { type: 'string', description: 'The task description for the sub-agent to execute.' },
          context: { type: 'string', description: 'Optional additional context or reference data for the sub-agent.' }
        }
      }
    }
  },
  // ══════════════════════════════════════════════
  // 新增工具：Plan Mode 执行追踪
  // ══════════════════════════════════════════════
  {
    type: 'function',
    function: {
      name: 'plan_track',
      description: 'Manage Plan Mode execution progress. Use this to mark plan steps as completed so you always know what remains. REQUIRED in Plan Mode — call plan_track after completing each step.',
      parameters: {
        type: 'object',
        required: ['action'],
        properties: {
          action: { type: 'string', enum: ['status', 'mark_done', 'mark_undone', 'mark_all_done'], description: 'status=查看当前进度, mark_done=标记步骤完成, mark_undone=撤销步骤完成标记, mark_all_done=全部完成' },
          step_index: { type: 'integer', description: 'Step number (1-indexed) to mark as done. Required for mark_done.' },
          step_label: { type: 'string', description: 'Optional: description of what was completed, for logging.' }
        }
      }
    }
  },
  // ══════════════════════════════════════════════
  // v5.0 新增工具：浏览器控制
  // ══════════════════════════════════════════════
  // v5.1 Browser 控制工具（增强版）
  // ══════════════════════════════════════════════
  {
    type: 'function',
    function: {
      name: 'browser_open',
      description: 'Open a URL in the agent browser. Waits for page load. Use wait_selector to wait for a specific element (useful for SPA pages). Returns page title and URL.',
      parameters: {
        type: 'object',
        required: ['url'],
        properties: {
          url: { type: 'string', description: 'The URL to open (http/https).' },
          wait_selector: { type: 'string', description: 'CSS selector to wait for after page load. Useful for SPA/dynamic pages. Default: none.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'browser_screenshot',
      description: 'Take a screenshot of the current page. Supports viewport (default), full_page (entire scrollable page), or selector (specific element only). Returns base64 PNG.',
      parameters: {
        type: 'object',
        properties: {
          full_page: { type: 'boolean', description: 'Capture the entire scrollable page. Default: false.' },
          selector: { type: 'string', description: 'CSS selector of element to capture. Overrides full_page.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'browser_evaluate',
      description: 'Execute JavaScript in the browser page. Returns the result as JSON string. Use to read DOM, extract data, or interact programmatically.',
      parameters: {
        type: 'object',
        required: ['js'],
        properties: {
          js: { type: 'string', description: 'JavaScript code to execute in the page context.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'browser_extract',
      description: 'Extract text and links from the current page. Use selector to extract only from a specific element (e.g. "main", "#content", "article"). Without selector, extracts full page. Returns title, text, and up to 50 links.',
      parameters: {
        type: 'object',
        properties: {
          selector: { type: 'string', description: 'CSS selector to extract from. Default: entire body.' },
          max_chars: { type: 'integer', description: 'Max characters to return. Default: 15000.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'browser_click',
      description: 'Click an element by CSS selector. Use wait=true to wait up to 10s for the element to appear first (useful after page navigation). Scrolls element into view before clicking.',
      parameters: {
        type: 'object',
        required: ['selector'],
        properties: {
          selector: { type: 'string', description: 'CSS selector for the element to click.' },
          wait: { type: 'boolean', description: 'Wait for element to appear before clicking (max 10s). Default: false.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'browser_type',
      description: 'Type text into an input field. Use clear=false to append instead of replacing. Use submit=true to press Enter or submit the parent form after typing.',
      parameters: {
        type: 'object',
        required: ['selector', 'text'],
        properties: {
          selector: { type: 'string', description: 'CSS selector for the input element.' },
          text: { type: 'string', description: 'Text to type into the input.' },
          clear: { type: 'boolean', description: 'Clear existing text first. Default: true.' },
          submit: { type: 'boolean', description: 'Submit the form or press Enter after typing. Default: false.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'browser_scroll',
      description: 'Scroll the page. Use direction for up/down/top/bottom, or selector to scroll a specific element into view.',
      parameters: {
        type: 'object',
        properties: {
          direction: { type: 'string', enum: ['down', 'up', 'top', 'bottom'], description: 'Scroll direction. Default: down.' },
          selector: { type: 'string', description: 'CSS selector to scroll into view (overrides direction).' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'browser_wait',
      description: 'Wait for a condition. Use selector to wait for an element to appear, or time_ms for a fixed delay. Default: 1 second.',
      parameters: {
        type: 'object',
        properties: {
          selector: { type: 'string', description: 'CSS selector to wait for. Returns found: true/false.' },
          time_ms: { type: 'integer', description: 'Fixed wait time in milliseconds. Default: 1000.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'browser_close',
      description: 'Close the agent browser and free resources.',
      parameters: { type: 'object', properties: {} }
    }
  },
  {
    type: 'function',
    function: {
      name: 'datetime',
      description: 'Get the precise current system time. Returns ISO timestamp, Unix time (seconds and milliseconds), locale-formatted date/time, timezone, and individual components (year/month/day/hour/minute/second/millisecond/day_of_week). Use format parameter to request specific output: "iso" / "unix" / "date" / "time" / "full" (default: full).',
      parameters: {
        type: 'object',
        properties: {
          format: { type: 'string', enum: ['full', 'iso', 'unix', 'date', 'time'], description: 'Output format. Default: full (all fields).' },
          timezone: { type: 'string', description: 'IANA timezone name (e.g., "Asia/Shanghai", "America/New_York"). Default: system timezone.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'calculator',
      description: 'Safely evaluate a mathematical expression. Supports + - * / ** % () and floating-point numbers. Uses a pure JS recursive descent parser — no eval(), CSP-safe. Returns the numeric result.',
      parameters: {
        type: 'object',
        required: ['expression'],
        properties: {
          expression: { type: 'string', description: 'The mathematical expression to evaluate (e.g., "(3 + 5) * 2 ** 3"). Max 500 characters.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'random',
      description: 'Generate random numbers or pick random items. Supports: int (integer range, default 0-100), float (decimal, default 0-1), pick (select from array of items), string (random alphanumeric, default length 8). Use count for multiple values.',
      parameters: {
        type: 'object',
        properties: {
          type: { type: 'string', enum: ['int', 'float', 'pick', 'string'], description: 'Random type. Default: int.' },
          min: { type: 'number', description: 'Minimum value (int/float). Default: 0.' },
          max: { type: 'number', description: 'Maximum value (int/float). Default: 100 (int) or 1 (float).' },
          count: { type: 'integer', description: 'Number of results. Max 100. Default: 1.' },
          items: { type: 'array', items: { type: 'string' }, description: 'Item pool for pick type.' },
          length: { type: 'integer', description: 'String length for string type. Max 256. Default: 8.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'uuid',
      description: 'Generate cryptographically random UUID v4 (e.g., "550e8400-e29b-41d4-a716-446655440000"). Uses Node.js crypto.randomUUID(). Supports batch generation up to 20.',
      parameters: {
        type: 'object',
        properties: {
          count: { type: 'integer', description: 'Number of UUIDs. Max 20. Default: 1.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'json_format',
      description: 'Format and validate a JSON string. Returns pretty-printed JSON with configurable indentation. Optionally sort object keys alphabetically. Also works as a JSON syntax validator — returns error details on invalid input.',
      parameters: {
        type: 'object',
        required: ['json'],
        properties: {
          json: { type: 'string', description: 'The JSON string to format/validate.' },
          indent: { type: 'integer', description: 'Indentation spaces. Default: 2.' },
          sort_keys: { type: 'boolean', description: 'Sort object keys alphabetically. Default: false.' }
        }
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'hash',
      description: 'Compute cryptographic hash of text. Supports MD5, SHA-1, SHA-256, SHA-384, SHA-512. Default: SHA-256. Returns hex-encoded digest.',
      parameters: {
        type: 'object',
        required: ['text'],
        properties: {
          text: { type: 'string', description: 'The text to hash.' },
          algorithm: { type: 'string', enum: ['md5', 'sha1', 'sha256', 'sha384', 'sha512'], description: 'Hash algorithm. Default: sha256.' }
        }
      }
    }
  }
];

// 支持三档开关的工具列表（auto/confirm/disabled）
// 浏览器工具不需要确认，永远自动执行
const MODE_TOOLS = [
  'run_command',
  'write_file', 'create_directory', 'delete_file',
  'edit_file', 'replace_in_files', 'move_file', 'copy_file',
  'download_file', 'compress',
];

export type ToolMode = 'auto' | 'confirm' | 'disabled';

// 工具模式缓存：key=工具名, value=模式
const _toolModes = new Map<string, ToolMode>();

export function needsConfirmation(toolName: string): boolean {
  if (!MODE_TOOLS.includes(toolName)) return false;
  return getToolMode(toolName) === 'confirm';
}

/** 获取工具模式，默认 confirm */
export function getToolMode(toolName: string): ToolMode {
  return _toolModes.get(toolName) ?? 'confirm';
}

/** 设置工具执行模式 */
export function setToolMode(toolName: string, mode: ToolMode): void {
  _toolModes.set(toolName, mode);
  if (mode === 'disabled') {
    setToolEnabled(toolName, false);
  } else {
    setToolEnabled(toolName, true);
  }
}

/** 兼容旧接口：设置 run_command 执行模式 */
export function setRunCommandMode(mode: ToolMode): void {
  setToolMode('run_command', mode);
}

/** 初始化全局工具模式（从数据库加载） */
export function initGlobalToolMode(mode: ToolMode): void {
  for (const toolName of MODE_TOOLS) {
    setToolMode(toolName, mode);
  }
}

let enabledTools: Set<string> = new Set([
  'read_file', 'list_directory', 'search_files',
  'write_file', 'create_directory', 'delete_file',
  'run_command',
  'move_file', 'copy_file', 'web_fetch', 'web_search', 'edit_file',
  'get_file_info', 'tree', 'download_file', 'diff_files', 'replace_in_files',
  'read_multiple_files', 'git', 'compress',
  'memory',
  'session_list', 'session_read', 'spawn_task',
  'browser_open', 'browser_screenshot', 'browser_evaluate', 'browser_extract',
  'browser_click', 'browser_type', 'browser_scroll', 'browser_wait', 'browser_close',
  'datetime', 'calculator',
  'random', 'uuid', 'json_format', 'hash'
]);

export function setToolEnabled(toolName: string, enabled: boolean): void {
  if (enabled) enabledTools.add(toolName);
  else enabledTools.delete(toolName);
}

/** Plan Mode 激活时注册 plan_track，关闭时移除 */
export function setPlanModeActive(active: boolean): void {
  if (active) {
    enabledTools.add('plan_track');
  } else {
    enabledTools.delete('plan_track');
    // 清除追踪器状态（同时清模块变量和 state）
    clearPlanTracker();
  }
}


export function getEnabledToolDefinitions(): ToolDefinition[] {
  const builtIn = TOOL_DEFINITIONS.filter(def => enabledTools.has(def.function.name));
  return [...builtIn, ..._mcpToolCache];
}

// R55: 语义工具检索 — 根据用户查询过滤相关工具，减少上下文占用

/** 核心工具集 — 始终包含的关键工具 */
const CORE_TOOLS = new Set([
  'read_file', 'write_file', 'list_directory', 'search_files', 'edit_file',
  'run_command', 'web_search', 'web_fetch', 'memory', 'git',
]);

/** 工具关键词映射 — 用于语义匹配 */
const TOOL_KEYWORDS: Record<string, string[]> = {
  read_file: ['read', 'file', '读取', '文件', '看', '查看', '内容'],
  write_file: ['write', 'file', '写入', '保存', '创建文件', '输出'],
  list_directory: ['list', 'directory', '目录', '文件夹', '列'],
  search_files: ['search', 'find', '搜索', '查找', 'grep', 'find'],
  create_directory: ['create', 'directory', 'mkdir', '创建目录', '新建'],
  delete_file: ['delete', 'remove', '删除', 'rm', '移除'],
  run_command: ['run', 'command', 'shell', '执行', '命令', '终端'],
  move_file: ['move', 'rename', '移动', '重命名', 'mv'],
  copy_file: ['copy', '复制', 'cp'],
  web_fetch: ['fetch', 'url', '网页', '抓取', '获取'],
  web_search: ['search', 'web', '搜索', '联网', '查', 'google', 'bing'],
  edit_file: ['edit', 'replace', '编辑', '替换', '修改'],
  get_file_info: ['info', 'stat', '信息', '属性', '详情'],
  tree: ['tree', '结构', '树', '目录结构'],
  download_file: ['download', '下载'],
  diff_files: ['diff', 'compare', '对比', '差异'],
  replace_in_files: ['replace', 'batch', '批量替换'],
  read_multiple_files: ['read', 'multiple', '批量读取'],
  git: ['git', 'commit', 'push', 'pull', 'branch', '仓库'],
  compress: ['compress', 'zip', 'tar', '压缩', '解压', 'extract'],
  memory: ['memory', '记忆', 'remember', 'save', 'rule', '规则'],
  session_list: ['session', 'history', '会话', '历史'],
  session_read: ['session', 'read', '读取会话'],
  spawn_task: ['sub', 'agent', 'delegate', '子代理', '委派'],
  plan_track: ['plan', 'track', '计划', '进度'],
  browser_open: ['browser', 'open', '浏览器', '打开网页'],
  browser_screenshot: ['screenshot', '截图', '屏幕'],
  browser_evaluate: ['evaluate', 'javascript', 'js', '执行JS'],
  browser_extract: ['extract', '提取', '内容'],
  browser_click: ['click', '点击'],
  browser_type: ['type', 'input', '输入'],
  browser_scroll: ['scroll', '滚动'],
  browser_wait: ['wait', '等待'],
  browser_close: ['close', '关闭浏览器'],
  datetime: ['time', 'date', '时间', '日期'],
  calculator: ['calculate', 'math', '计算', '算'],
  random: ['random', '随机'],
  uuid: ['uuid', 'guid', '唯一'],
  json_format: ['json', 'format', '格式化'],
  hash: ['hash', 'sha', 'md5', '哈希'],
};

/** R55: 根据用户查询语义过滤工具定义，减少 token 占用 */
export function getRelevantToolDefinitions(userQuery: string): ToolDefinition[] {
  const allTools = getEnabledToolDefinitions();
  // 查询为空或很短时返回全部
  if (!userQuery || userQuery.length < 5) return allTools;

  const queryLower = userQuery.toLowerCase();
  const relevantTools = new Set<string>();

  // 核心工具始终包含
  for (const t of CORE_TOOLS) relevantTools.add(t);

  // 语义匹配
  for (const [toolName, keywords] of Object.entries(TOOL_KEYWORDS)) {
    if (relevantTools.has(toolName)) continue;
    for (const kw of keywords) {
      if (queryLower.includes(kw.toLowerCase())) {
        relevantTools.add(toolName);
        break;
      }
    }
  }

  // 如果匹配到的工具太少，返回全部（避免遗漏）
  if (relevantTools.size < 8) return allTools;

  const filtered = allTools.filter(t => relevantTools.has(t.function.name));
  // 确保不过度过滤 — 至少保留 60% 的工具
  if (filtered.length < allTools.length * 0.6) return allTools;

  return filtered;
}

/** MCP 工具缓存 */
let _mcpToolCache: ToolDefinition[] = [];

/** 刷新 MCP 工具缓存（在 MCP 服务器配置变更时调用） */
export async function refreshMCPTools(): Promise<void> {
  try {
    const rawTools = await getMCPToolDefinitions();
    // v5.1.2 Shadowing 防护：MCP 工具不能覆盖内置工具
    const builtInNames = new Set(TOOL_DEFINITIONS.map(d => d.function.name));
    const filtered = rawTools.filter(t => {
      if (builtInNames.has(t.function.name)) {
        logWarn(`MCP 工具 "${t.function.name}" 与内置工具重名，已跳过`);
        return false;
      }
      return true;
    });
    const skipped = rawTools.length - filtered.length;
    _mcpToolCache = filtered;
    if (filtered.length > 0) {
      logInfo(`MCP 工具已注册: ${filtered.length} 个${skipped > 0 ? `（${skipped} 个因重名被跳过）` : ''}`, filtered.map(t => t.function.name).join(', '));
    }
  } catch (err) {
    logError('MCP 工具刷新失败', (err as Error).message);
    _mcpToolCache = [];
  }
}

// ══════════════════════════════════════════════
// R21-R30: 工具系统增强
// ══════════════════════════════════════════════

// ── R28: 工具安全检查增强 ──

/** R28: 路径遍历攻击检测 — 检查路径中是否包含恶意 ../ 序列 */
function hasPathTraversal(path: string): boolean {
  if (!path) return false;
  // 检测 ../ 或 ..\ 模式（路径遍历攻击）
  const normalized = path.replace(/\\/g, '/');
  // 连续的 ../ 超过 2 层（正常相对路径很少超过 2 层）
  const traversalCount = (normalized.match(/\.\.\//g) || []).length;
  if (traversalCount > 3) return true;
  // 检测 ..\..\..\ 模式
  if (normalized.includes('../../../')) return true;
  return false;
}

/** R28: 命令注入检测 — 检查命令中是否包含危险的 shell 注入模式 */
function hasCommandInjection(command: string): boolean {
  if (!command) return false;
  const dangerousPatterns = [
    /;\s*(rm|del|format|mkfs|dd)\s/i,       // 命令链中的删除操作
    /\|\s*(rm|del|format|mkfs)\s/i,         // 管道到删除操作
    /&&\s*(rm|del|format|mkfs)\s/i,         // AND链中的删除操作
    /\$\{.*IFS.*\}/i,                        // IFS 变量注入
    /\$\([^)]*\)/,                           // 命令替换 $(...)
    /`[^`]*`/,                               // 反引号命令替换
    /\x00/,                                  // null 字节注入
  ];
  return dangerousPatterns.some(p => p.test(command));
}

/** R28: 工具安全验证 — 在执行前检查安全风险 */
export function validateToolSecurity(toolName: string, args: Record<string, unknown>): string | null {
  // 路径遍历检查
  const pathFields = ['path', 'source', 'destination', 'file1', 'file2', 'cwd'];
  for (const field of pathFields) {
    if (typeof args[field] === 'string' && hasPathTraversal(args[field] as string)) {
      return `安全警告: 参数 "${field}" 包含可疑的路径遍历模式: ${args[field]}`;
    }
  }

  // 多路径参数检查（read_multiple_files）
  if (Array.isArray(args.paths)) {
    for (const p of args.paths) {
      if (typeof p === 'string' && hasPathTraversal(p)) {
        return `安全警告: paths 数组中包含可疑的路径遍历模式: ${p}`;
      }
    }
  }

  // 命令注入检查
  if (toolName === 'run_command' && typeof args.command === 'string') {
    if (hasCommandInjection(args.command as string)) {
      return `安全警告: 命令中包含潜在的注入模式: ${args.command}`;
    }
  }

  // web_fetch / download_file URL 验证
  const urlFields = ['url'];
  for (const field of urlFields) {
    if (typeof args[field] === 'string') {
      const url = args[field] as string;
      // 阻止 file:// 协议（可能读取敏感本地文件）
      if (url.toLowerCase().startsWith('file://')) {
        return `安全警告: 不允许 file:// 协议`;
      }
      // 阻止内网 IP 访问（可选，取决于安全策略）
      // 192.168.x.x, 10.x.x.x, 172.16-31.x.x
      const internalIpPattern = /^(https?:\/\/)?(192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[01])\.|127\.|localhost)/i;
      if (internalIpPattern.test(url)) {
        // 内网访问仅警告不阻止
        logWarn(`R28: 访问内网地址: ${url}`);
      }
    }
  }

  return null; // 无安全风险
}

// ── R30: 工具使用统计导出 ──

/** R30: 工具使用统计热力图数据 */
export interface ToolHeatmapData {
  toolName: string;
  displayName: string;
  icon: string;
  totalCalls: number;
  successRate: number;
  avgDurationMs: number;
  heatLevel: number;  // 0-4, 0=未使用, 4=最常用
  lastUsed: number;
  errorCount: number;
}

/** R30: 获取工具使用热力图数据 */
export function getToolHeatmap(): ToolHeatmapData[] {
  const analytics = getToolAnalytics();
  const maxCalls = Math.max(1, ...analytics.map(a => a.totalCalls));

  // 合并所有已定义工具（包括未使用的）
  const allTools = new Map<string, ToolHeatmapData>();
  for (const def of TOOL_DEFINITIONS) {
    const name = def.function.name;
    allTools.set(name, {
      toolName: name,
      displayName: formatToolName(name),
      icon: getToolIcon(name),
      totalCalls: 0,
      successRate: 0,
      avgDurationMs: 0,
      heatLevel: 0,
      lastUsed: 0,
      errorCount: 0,
    });
  }

  // 填入分析数据
  for (const a of analytics) {
    const entry = allTools.get(a.toolName);
    if (entry) {
      entry.totalCalls = a.totalCalls;
      entry.successRate = a.successRate;
      entry.avgDurationMs = a.avgDurationMs;
      entry.lastUsed = a.lastUsed;
      entry.errorCount = a.errorCount;
    } else {
      // MCP 工具或未定义工具
      allTools.set(a.toolName, {
        toolName: a.toolName,
        displayName: a.toolName,
        icon: '🔧',
        totalCalls: a.totalCalls,
        successRate: a.successRate,
        avgDurationMs: a.avgDurationMs,
        heatLevel: 0,
        lastUsed: a.lastUsed,
        errorCount: a.errorCount,
      });
    }
  }

  // 计算热力等级
  const result = Array.from(allTools.values());
  for (const entry of result) {
    if (entry.totalCalls === 0) {
      entry.heatLevel = 0;
    } else {
      const ratio = entry.totalCalls / maxCalls;
      if (ratio > 0.75) entry.heatLevel = 4;
      else if (ratio > 0.5) entry.heatLevel = 3;
      else if (ratio > 0.25) entry.heatLevel = 2;
      else entry.heatLevel = 1;
    }
  }

  // 按使用次数降序排列
  result.sort((a, b) => b.totalCalls - a.totalCalls);
  return result;
}

// ── R21: 工具参数验证 ──

/** R21: 根据 schema 验证工具参数，返回错误消息列表 */
export function validateToolArgs(toolName: string, args: Record<string, unknown>): string[] {
  const def = TOOL_DEFINITIONS.find(d => d.function.name === toolName);
  if (!def) return []; // MCP 工具或未找到定义，跳过验证

  const params = def.function.parameters;
  const errors: string[] = [];

  // 检查必填参数
  if (params.required) {
    for (const req of params.required) {
      if (!(req in args) || args[req] === undefined || args[req] === null) {
        errors.push(`缺少必填参数: "${req}"`);
      }
    }
  }

  // 检查参数类型和枚举值
  for (const [key, value] of Object.entries(args)) {
    if (value === undefined || value === null) continue;
    const prop = params.properties[key];
    if (!prop) continue; // 未定义的参数，跳过

    // 类型检查
    if (prop.type === 'string' && typeof value !== 'string') {
      errors.push(`参数 "${key}" 应为字符串类型，实际为 ${typeof value}`);
    } else if (prop.type === 'integer' && (!Number.isInteger(Number(value)) || typeof value === 'boolean')) {
      errors.push(`参数 "${key}" 应为整数，实际为 ${JSON.stringify(value)}`);
    } else if (prop.type === 'boolean' && typeof value !== 'boolean') {
      errors.push(`参数 "${key}" 应为布尔值，实际为 ${typeof value}`);
    } else if (prop.type === 'array' && !Array.isArray(value)) {
      errors.push(`参数 "${key}" 应为数组，实际为 ${typeof value}`);
    } else if (prop.type === 'object' && (typeof value !== 'object' || Array.isArray(value))) {
      errors.push(`参数 "${key}" 应为对象，实际为 ${typeof value}`);
    }

    // 枚举值检查
    if (prop.enum && !prop.enum.includes(String(value))) {
      errors.push(`参数 "${key}" 值 "${value}" 不在允许范围内: ${prop.enum.join(', ')}`);
    }
  }

  return errors;
}

// ── R22: 工具结果截断 ──

/** R22: 工具结果最大字符数 */
const MAX_TOOL_RESULT_CHARS = 30000;

/** R22: 截断大输出结果，保留首尾并添加截断标记 */
export function truncateToolResult(result: ToolResult, toolName: string): ToolResult {
  const jsonStr = JSON.stringify(result);
  if (jsonStr.length <= MAX_TOOL_RESULT_CHARS) return result;

  // 不同工具有不同的截断策略
  const truncated = { ...result };

  // 截断 stdout/content 类的大字段
  const largeFields = ['stdout', 'content', 'text', 'result', 'output', 'data'];
  for (const field of largeFields) {
    if (typeof truncated[field] === 'string' && (truncated[field] as string).length > 10000) {
      const original = truncated[field] as string;
      const head = original.slice(0, 8000);
      const tail = original.slice(-3000);
      const omitted = original.length - 11000;
      truncated[field] = `${head}\n\n... [已截断 ${omitted} 字符，共 ${original.length} 字符] ...\n\n${tail}`;
    }
  }

  // 截断数组类结果
  if (Array.isArray(truncated.entries) && truncated.entries.length > 100) {
    const total = truncated.entries.length;
    truncated.entries = truncated.entries.slice(0, 50);
    truncated._truncated = true;
    truncated._totalEntries = total;
    truncated._truncatedMessage = `结果已截断：显示前 50 条，共 ${total} 条`;
  }
  if (Array.isArray(truncated.results) && truncated.results.length > 100) {
    const total = truncated.results.length;
    truncated.results = truncated.results.slice(0, 50);
    truncated._truncated = true;
    truncated._totalResults = total;
  }

  logWarn(`R22: 工具 ${toolName} 结果已截断 (${jsonStr.length} → ~${JSON.stringify(truncated).length} 字符)`);
  return truncated;
}

// ── R23: 工具参数自动类型转换 ──

/** R23: 根据工具定义自动转换参数类型 */
export function coerceToolArgs(toolName: string, args: Record<string, unknown>): Record<string, unknown> {
  const def = TOOL_DEFINITIONS.find(d => d.function.name === toolName);
  if (!def) return args;

  const params = def.function.parameters;
  const coerced = { ...args };

  for (const [key, value] of Object.entries(coerced)) {
    if (value === undefined || value === null) continue;
    const prop = params.properties[key];
    if (!prop) continue;

    // 字符串数字 → 整数
    if (prop.type === 'integer' && typeof value === 'string') {
      const num = parseInt(value, 10);
      if (!isNaN(num)) {
        coerced[key] = num;
      }
    }
    // 字符串数字 → 数字
    else if (prop.type === 'number' && typeof value === 'string') {
      const num = parseFloat(value);
      if (!isNaN(num)) {
        coerced[key] = num;
      }
    }
    // 字符串布尔 → 布尔
    else if (prop.type === 'boolean' && typeof value === 'string') {
      if (value === 'true' || value === '1') coerced[key] = true;
      else if (value === 'false' || value === '0') coerced[key] = false;
    }
    // 数组：字符串 → 数组（逗号分隔）
    else if (prop.type === 'array' && typeof value === 'string') {
      try {
        coerced[key] = JSON.parse(value);
      } catch {
        // 如果不是 JSON，按逗号分隔
        coerced[key] = value.split(',').map(s => s.trim()).filter(Boolean);
      }
    }
    // 对象：字符串 → 对象
    else if (prop.type === 'object' && typeof value === 'string') {
      try {
        coerced[key] = JSON.parse(value);
      } catch {
        // 解析失败，保持原样
      }
    }
  }

  return coerced;
}

// ── R24: 工具错误恢复建议 ──

/** R24: 分析工具错误并给出修复建议 */
export function suggestToolFix(toolName: string, args: Record<string, unknown>, error: string): string {
  const err = error.toLowerCase();

  // 文件未找到
  if (err.includes('no such file') || err.includes('not found') || err.includes('文件不存在') || err.includes('enoent')) {
    const path = args.path || args.source || args.file1 || '';
    return `文件路径可能不正确: "${path}"。建议：1) 使用 list_directory 检查路径是否存在; 2) 检查拼写; 3) 尝试绝对路径。`;
  }

  // 权限拒绝
  if (err.includes('permission denied') || err.includes('eacces') || err.includes('权限')) {
    return `权限不足。建议：1) 检查文件/目录权限; 2) 确认工作空间路径是否正确; 3) 某些系统目录可能需要管理员权限。`;
  }

  // 路径无效
  if (err.includes('invalid path') || err.includes('illegal characters') || err.includes('路径无效')) {
    const path = args.path || args.destination || '';
    return `路径格式可能不正确: "${path}"。建议：1) 检查特殊字符; 2) Windows 路径使用反斜杠或正斜杠; 3) 确认路径在工作空间内。`;
  }

  // 网络错误
  if (err.includes('network') || err.includes('timeout') || err.includes('econnrefused') || err.includes('fetch')) {
    return `网络请求失败。建议：1) 检查 URL 是否正确; 2) 确认网络连接; 3) 某些站点可能需要使用 browser_open 工具代替 web_fetch。`;
  }

  // JSON 解析错误
  if (err.includes('json') || err.includes('parse') || err.includes('syntax')) {
    return `数据解析失败。建议：1) 使用 json_format 工具验证 JSON 格式; 2) 检查参数是否为有效的 JSON 字符串。`;
  }

  // Git 错误
  if (err.includes('git') || err.includes('not a repository')) {
    return `Git 操作失败。建议：1) 确认当前目录是 Git 仓库; 2) 使用 git status 检查仓库状态; 3) 检查分支名是否正确。`;
  }

  // 命令执行失败
  if (err.includes('command') || err.includes('exit code')) {
    return `命令执行失败。建议：1) 检查命令拼写; 2) 确认命令在当前系统可用; 3) 使用 cwd 参数指定正确的工作目录。`;
  }

  // 通用建议
  return '';
}

// ── R25: 工具执行分析追踪 ──

export interface ToolAnalytics {
  toolName: string;
  totalCalls: number;
  successCount: number;
  errorCount: number;
  avgDurationMs: number;
  lastUsed: number;
  commonErrors: Map<string, number>;
}

const _toolAnalytics = new Map<string, ToolAnalytics>();

/** R25: 记录工具执行分析数据 */
function recordToolAnalytics(toolName: string, success: boolean, durationMs: number, error?: string): void {
  let analytics = _toolAnalytics.get(toolName);
  if (!analytics) {
    analytics = {
      toolName,
      totalCalls: 0,
      successCount: 0,
      errorCount: 0,
      avgDurationMs: 0,
      lastUsed: 0,
      commonErrors: new Map(),
    };
    _toolAnalytics.set(toolName, analytics);
  }

  analytics.totalCalls++;
  if (success) analytics.successCount++;
  else {
    analytics.errorCount++;
    if (error) {
      // 记录常见错误模式（取前 100 字符作为 key）
      const errorKey = error.slice(0, 100);
      analytics.commonErrors.set(errorKey, (analytics.commonErrors.get(errorKey) || 0) + 1);
    }
  }

  // 更新平均执行时间（指数移动平均）
  const alpha = 0.2;
  analytics.avgDurationMs = analytics.avgDurationMs === 0
    ? durationMs
    : analytics.avgDurationMs * (1 - alpha) + durationMs * alpha;

  analytics.lastUsed = Date.now();
}

/** R25: 获取工具分析数据 */
export function getToolAnalytics(): Array<ToolAnalytics & { successRate: number; commonErrorsArray: Array<{ error: string; count: number }> }> {
  return Array.from(_toolAnalytics.values()).map(a => ({
    ...a,
    successRate: a.totalCalls > 0 ? a.successCount / a.totalCalls : 0,
    commonErrorsArray: Array.from(a.commonErrors.entries())
      .map(([error, count]) => ({ error, count }))
      .sort((a, b) => b.count - a.count)
      .slice(0, 5),
    commonErrors: undefined as any, // 避免序列化 Map
  }));
}

/** R25: 重置工具分析数据 */
export function resetToolAnalytics(): void {
  _toolAnalytics.clear();
}

// ── R26: 工具结果格式优化 ──

/** R26: 优化工具结果格式，提升 LLM 理解能力 */
function formatToolResult(toolName: string, result: ToolResult): ToolResult {
  if (!result.success) return result;

  // 为特定工具添加结构化摘要
  switch (toolName) {
    case 'list_directory':
    case 'tree': {
      // 目录列表添加统计摘要
      const entries = (result as any).entries || (result as any).items;
      if (Array.isArray(entries)) {
        const dirs = entries.filter((e: any) => e.type === 'directory').length;
        const files = entries.filter((e: any) => e.type === 'file').length;
        (result as any)._summary = `共 ${entries.length} 项 (${dirs} 个目录, ${files} 个文件)`;
      }
      break;
    }
    case 'search_files': {
      const results = (result as any).results || (result as any).matches;
      if (Array.isArray(results)) {
        (result as any)._summary = `找到 ${results.length} 个匹配结果`;
      }
      break;
    }
    case 'read_file': {
      // 读取文件添加行数统计
      const content = (result as any).content || '';
      if (typeof content === 'string') {
        const lines = content.split('\n').length;
        const chars = content.length;
        (result as any)._meta = `文件内容: ${lines} 行, ${chars} 字符`;
      }
      break;
    }
    case 'web_search': {
      const results = (result as any).results;
      if (Array.isArray(results)) {
        (result as any)._summary = `搜索返回 ${results.length} 条结果`;
      }
      break;
    }
  }

  return result;
}

export async function executeTool(toolName: string, args: Record<string, unknown>): Promise<ToolResult> {
  // MCP 工具路由
  if (toolName.startsWith('mcp_')) {
    const { executeMCPTool } = await import('./mcp-client.js');
    return executeMCPTool(toolName, args);
  }

  if (!enabledTools.has(toolName)) {
    logError(`工具未启用: ${toolName}`);
    return { success: false, error: `工具 ${toolName} 未启用` };
  }

  const bridge = window.metonaDesktop;
  if (!bridge?.isDesktop) {
    logError(`工具调用仅支持桌面版: ${toolName}`);
    return { success: false, error: '工具调用仅支持桌面版' };
  }

  // R23: 参数自动类型转换
  args = coerceToolArgs(toolName, args);

  // R21: 参数验证
  const validationErrors = validateToolArgs(toolName, args);
  if (validationErrors.length > 0) {
    logError(`工具参数验证失败: ${toolName}`, validationErrors.join('; '));
    return { success: false, error: `参数验证失败: ${validationErrors.join('; ')}` };
  }

  // R28: 安全验证 — 路径遍历、命令注入防护
  const securityWarning = validateToolSecurity(toolName, args);
  if (securityWarning) {
    logError(`R28: 工具安全拦截: ${toolName}`, securityWarning);
    return { success: false, error: securityWarning };
  }

  const startTime = Date.now();

  try {
    // 内存工具（不走 IPC，直接处理 MEMORY.md）
    if (toolName === 'memory') {
      const { search, addEntry, replaceEntry, removeEntry, removeEntries, loadAllEntries, DuplicateEntryError } = await import('./memory-service.js');
      const action = args.action as string;
      switch (action) {
        case 'search': {
          const query = args.query as string;
const limit = (args.limit as number) || 0; // 0 = 不限制
if (!query) return { success: false, error: '缺少 query 参数' };
const results = await search(query, limit);
          logToolResult('memory', true, `${results.length} 条结果`);
          return { success: true, action, results, total: results.length };
        }
        case 'add': {
          const type = (args.type as 'fact' | 'preference' | 'rule') || 'fact';  // 兜底默认 fact
          const content = args.content as string;
          const importance = (args.importance as number) || 5;
          const tags = (args.tags as string[]) || [];
          if (!content || content.length < 2) return { success: false, error: 'add 操作缺少 content 参数或内容太短。示例: {"action":"add","type":"fact","content":"具体的记忆内容"}' };
          try {
            const entry = await addEntry(type, content, importance, tags);
            logToolResult('memory', true, `${type}: ${content.slice(0, 50)}`);
            return { success: true, action, id: entry.id, type: entry.type, content: entry.content };
          } catch (err) {
            if (err instanceof DuplicateEntryError) {
              const dupErr = err as InstanceType<typeof DuplicateEntryError>;
              logToolResult('memory', true, `重复跳过: ${dupErr.existingPreview}`);
              return { success: true, action, duplicate: true, existing_id: dupErr.existingId, message: `已跳过: 相同内容的记忆已存在 (${dupErr.existingPreview})。不要再重复添加，任务已完成。` };
            }
            throw err;
          }
        }
        case 'replace': {
          const oldText = String(args.old_text || '');
          const newContent = String(args.new_content || '');
          const result = await replaceEntry(oldText, newContent);
          logToolResult('memory', result.success, result.message);
          return { success: result.success, action, ...(result.success ? {} : { error: result.message }), message: result.message };
        }
        case 'remove': {
          const oldText = String(args.old_text || '');
          const result = await removeEntry(oldText);
          logToolResult('memory', result.success, result.message);
          return { success: result.success, action, ...(result.success ? {} : { error: result.message }), message: result.message };
        }
        case 'remove_batch': {
          const oldTexts = Array.isArray(args.old_texts) ? args.old_texts.map(String) : [];
          if (oldTexts.length === 0) return { success: false, error: '缺少 old_texts 参数（字符串数组）' };
          const result = await removeEntries(oldTexts);
          logToolResult('memory', result.success, result.message);
          return { success: result.success, action, results: result.results, deleted: result.deleted, failed: result.failed, ...(result.success ? {} : { error: result.message }), message: result.message };
        }
        case 'read_all': {
          const entries = await loadAllEntries();
          logToolResult('memory', true, `${entries.length} 条记忆`);
          return { success: true, action, entries, total: entries.length };
        }
        default:
          return { success: false, error: `未知操作: ${action}。支持: search / add / replace / remove / read_all` };
      }
    }
    if (toolName === 'session_list') {
      const bridge = window.metonaDesktop;
      if (!bridge?.db) return { success: false, error: '桌面 API 不可用' };
      const limit = (args.limit as number) || 0; // 0 = 不限制
      const search = (args.search as string) || '';
      const sessions = await bridge.db.getAllSessions();
      let filtered = sessions.map((s: any) => ({
        id: s.id,
        title: s.title,
        model: s.model,
        messageCount: 0, // 从 SQLite 获取的原始行不含 messages
        createdAt: s.created_at,
        updatedAt: s.updated_at
      }));
      if (search) {
        filtered = filtered.filter((s: any) => s.title.toLowerCase().includes(search.toLowerCase()));
      }
      filtered.sort((a: any, b: any) => b.updatedAt - a.updatedAt);
      if (limit > 0) filtered = filtered.slice(0, limit);
      logToolResult('session_list', true, `${filtered.length} 个会话`);
      return { success: true, sessions: filtered, total: filtered.length };
    }
    if (toolName === 'session_read') {
      const bridge = window.metonaDesktop;
      if (!bridge?.db) return { success: false, error: '桌面 API 不可用' };
      const sessionId = args.session_id as string;
      const maxMessages = (args.max_messages as number) || 0; // 0 = 不限制
      if (!sessionId) return { success: false, error: '缺少 session_id 参数' };
      const session = await bridge.db.getSession(sessionId);
      if (!session) return { success: false, error: `会话 ${sessionId} 不存在` };
      const messages = await bridge.db.getMessages(sessionId);
      const msgs = (maxMessages > 0 ? messages.slice(0, maxMessages) : messages).map((m: any) => ({
        role: m.role,
        content: m.content || '',
        timestamp: m.created_at
      }));
      logToolResult('session_read', true, `${msgs.length} 条消息`);
      return { success: true, session: { id: session.id, title: session.title, model: session.model }, messages: msgs, total: messages.length };
    }

    // v4.3 spawn_task 走子代理（模型只能从设置面板指定，不允许 AI 自选）
    if (toolName === 'spawn_task') {
      const { executeSubAgent } = await import('./sub-agent.js');
      const task = args.task as string;
      const context = args.context as string | undefined;
      // 只使用设置面板配置的子代理模型，忽略 AI 传入的 model 参数
      const configuredModel = state.get<string>('subAgentModel', '');
      let model: string | undefined;
      if (configuredModel) {
        // 设置面板存的模型名直接使用（面板加载时已验证过列表）
        model = configuredModel;
      }
      if (!task) return { success: false, error: '缺少 task 参数' };
      logInfo(`子代理委派: ${task.slice(0, 80)}${model ? ` (模型: ${model})` : ' (跟随当前模型)'}`);
      const result = await executeSubAgent(task, context, model ? { model } : {});
      logToolResult('spawn_task', result.success, result.success ? `完成, ${(result as any).loops} 轮` : result.error);
      return result;
    }

    // plan_track — Plan Mode 执行追踪
    if (toolName === 'plan_track') {
      const action = args.action as string;
      const stepIndex = args.step_index as number | undefined;
      const stepLabel = (args.step_label as string) || '';
      const tracker = getPlanTracker();
      if (action === 'status') {
        return { success: true, action: 'status', steps: tracker.steps, total: tracker.total, done: tracker.done };
      }
      if (action === 'mark_done' && typeof stepIndex === 'number' && stepIndex > 0) {
        if (!Number.isInteger(stepIndex)) {
          return { success: false, error: `step_index 必须是整数，收到 ${stepIndex}` };
        }
        const idx = stepIndex - 1;
        if (idx >= 0 && idx < tracker.steps.length) {
          tracker.steps[idx].done = true;
          tracker.done = tracker.steps.filter(s => s.done).length;
          if (stepLabel) tracker.steps[idx].label = stepLabel;
          savePlanTracker(tracker);
          const remaining = tracker.total - tracker.done;
          logToolResult('plan_track', true, `步骤 ${stepIndex} 已完成 (${tracker.done}/${tracker.total}${remaining > 0 ? `, 剩余 ${remaining}` : ', 全部完成!'})`);
          return { success: true, action: 'mark_done', step: stepIndex, done: tracker.done, total: tracker.total, remaining };
        }
        return { success: false, error: `步骤 ${stepIndex} 不存在（共 ${tracker.total} 步）` };
      }
      if (action === 'mark_undone' && typeof stepIndex === 'number' && stepIndex > 0) {
        if (!Number.isInteger(stepIndex)) {
          return { success: false, error: `step_index 必须是整数，收到 ${stepIndex}` };
        }
        const idx = stepIndex - 1;
        if (idx >= 0 && idx < tracker.steps.length) {
          tracker.steps[idx].done = false;
          tracker.done = tracker.steps.filter(s => s.done).length;
          savePlanTracker(tracker);
          const remaining = tracker.total - tracker.done;
          logToolResult('plan_track', true, `步骤 ${stepIndex} 已撤销完成标记 (${tracker.done}/${tracker.total}, 剩余 ${remaining})`);
          return { success: true, action: 'mark_undone', step: stepIndex, done: tracker.done, total: tracker.total, remaining };
        }
        return { success: false, error: `步骤 ${stepIndex} 不存在（共 ${tracker.total} 步）` };
      }
      if (action === 'mark_all_done') {
        for (const s of tracker.steps) s.done = true;
        tracker.done = tracker.total;
        savePlanTracker(tracker);
        logToolResult('plan_track', true, `全部 ${tracker.total} 步已完成`);
        return { success: true, action: 'mark_all_done', done: tracker.total, total: tracker.total, remaining: 0 };
      }
      return { success: false, error: `未知操作: ${action}` };
    }

    // run_command 走 workspace IPC
    if (toolName === 'run_command') {
      const command = args.command as string;
      const cwd = args.cwd as string | undefined;
      if (!command) {
        return { success: false, error: '缺少 command 参数' };
      }
      logInfo(`工作空间命令执行: ${command.slice(0, 100)}`);
      const result = await bridge.workspace.execTool(command, cwd);
      logToolResult('run_command', result.success, result.success ? undefined : result.stderr?.slice(0, 200));
      return {
        success: result.success,
        stdout: result.stdout,
        stderr: result.stderr,
        exitCode: result.exitCode,
        duration: result.duration
      };
    }

    // 其他工具：IPC 调用（无超时，由 renderer 内部处理）
    const result = await bridge.tool.execute(toolName, args);
    logToolResult(toolName, result.success, result.success ? undefined : result.error);

    // R22: 截断大输出结果
    const truncatedResult = truncateToolResult(result, toolName);
    // R26: 优化结果格式
    const formattedResult = formatToolResult(toolName, truncatedResult);

    // R25: 记录分析数据
    const duration = Date.now() - startTime;
    recordToolAnalytics(toolName, formattedResult.success, duration, formattedResult.error);

    return formattedResult;
  } catch (err) {
    const duration = Date.now() - startTime;
    const errorMsg = (err as Error).message;
    logError(`工具异常: ${toolName}`, errorMsg);

    // R24: 生成错误恢复建议
    const suggestion = suggestToolFix(toolName, args, errorMsg);
    // R25: 记录分析数据
    recordToolAnalytics(toolName, false, duration, errorMsg);

    return { success: false, error: suggestion ? `${errorMsg}\n\n💡 建议: ${suggestion}` : errorMsg };
  }
}

export function getToolIcon(name: string): string {
  const icons: Record<string, string> = {
    read_file: '📄', write_file: '✏️', list_directory: '📁',
    search_files: '🔍', create_directory: '📂', delete_file: '🗑️', run_command: '💻',
    move_file: '📦', copy_file: '📋', web_fetch: '🌐',
    edit_file: '✂️', get_file_info: 'ℹ️', tree: '🌳', download_file: '⬇️',
    diff_files: '🔀', replace_in_files: '🔄', read_multiple_files: '📚',
    git: '🔖', compress: '🗜️', web_search: '🔍',
    memory: '🧠',
    session_list: '📋', session_read: '📖', spawn_task: '🤖',
    plan_track: '📋',
    browser_open: '🌐', browser_screenshot: '📸', browser_evaluate: '⚡', browser_extract: '📰',
    browser_click: '👆', browser_type: '⌨️', browser_scroll: '↕️', browser_close: '❌',
    browser_wait: '⏳',
    datetime: '🕐', calculator: '🔢', random: '🎲', uuid: '🔑', json_format: '📝', hash: '#️⃣'
  };
  return icons[name] || '🔧';
}

/** ── Plan Mode 执行追踪器 ── */

export interface PlanStep {
  index: number;
  label: string;
  done: boolean;
}

export interface PlanTracker {
  steps: PlanStep[];
  total: number;
  done: number;
  active: boolean;
}

let _planTracker: PlanTracker = { steps: [], total: 0, done: 0, active: false };

const EMPTY_TRACKER: PlanTracker = { steps: [], total: 0, done: 0, active: false };

export function getPlanTracker(): PlanTracker {
  // 始终从 state 同步最新状态；state 为 null 时返回空追踪器
  const saved = state.get<PlanTracker | null>('_planTracker', null);
  _planTracker = saved ?? EMPTY_TRACKER;
  return _planTracker;
}

export function savePlanTracker(tracker: PlanTracker): void {
  _planTracker = tracker;
  state.set('_planTracker', tracker);
}

export function initPlanTracker(steps: string[]): PlanTracker {
  const tracker: PlanTracker = {
    steps: steps.map((label, i) => ({ index: i + 1, label, done: false })),
    total: steps.length,
    done: 0,
    active: true,
  };
  savePlanTracker(tracker);
  logInfo('Plan Tracker 已初始化', `${tracker.total} 个步骤`);
  return tracker;
}

export function clearPlanTracker(): void {
  _planTracker = EMPTY_TRACKER;
  state.set('_planTracker', null);
}

export function formatPlanStatus(): string {
  const t = getPlanTracker();
  if (!t.active || t.steps.length === 0) return '';
  const lines = t.steps.map(s => {
    const icon = s.done ? '✅' : '⬜';
    return `${icon} 步骤${s.index}: ${s.label}`;
  });
  const pct = t.total > 0 ? Math.round(t.done / t.total * 100) : 0;
  const hint = t.done >= t.total ? '✅ 全部步骤已完成，请给出最终回答。' : '';
  return `[Plan Mode 执行进度: ${t.done}/${t.total} (${pct}%)]\n${lines.join('\n')}${hint ? '\n\n' + hint : ''}`;
}

export function formatToolName(name: string): string {
  const names: Record<string, string> = {
    read_file: '读取文件', write_file: '写入文件', list_directory: '列出目录',
    search_files: '搜索文件', create_directory: '创建目录', delete_file: '删除文件',
    run_command: '执行命令', move_file: '移动文件', copy_file: '复制文件',
    web_fetch: '网页抓取', edit_file: '编辑文件',
    get_file_info: '文件信息', tree: '目录树', download_file: '下载文件',
    diff_files: '文件对比', replace_in_files: '批量替换', read_multiple_files: '批量读取',
    git: 'Git 操作', compress: '压缩/解压', web_search: '联网搜索',
    memory: '记忆管理',
    session_list: '会话列表', session_read: '读取会话', spawn_task: '子代理委派',
    plan_track: '计划追踪',
    browser_open: '打开网页', browser_screenshot: '浏览器截图', browser_evaluate: '执行JS', browser_extract: '提取内容',
    browser_click: '点击元素', browser_type: '输入文本', browser_scroll: '滚动页面', browser_close: '关闭浏览器',
    browser_wait: '等待',
    datetime: '日期时间', calculator: '计算器', random: '随机数', uuid: '生成UUID', json_format: 'JSON格式化', hash: '哈希计算'
  };
  return names[name] || name;
}
