'use strict';
const fs = require('fs');
const path = require('path');
const { execFile } = require('child_process');
const { promisify } = require('util');
const execFileAsync = promisify(execFile);

/**
 * Execute a tool call from Claude's tool_use response.
 * Uses execFile (not exec) to prevent shell injection.
 * @param {string} toolName - e.g., 'http_fetch', 'read_file', 'shell_exec'
 * @param {object} input - tool parameters from Claude
 * @returns {Promise<{success: boolean, result: any, error?: string}>}
 */
async function executeTool(toolName, input) {
  const executor = TOOL_EXECUTORS[toolName];
  if (!executor) {
    return { success: false, result: null, error: `Unknown tool: ${toolName}` };
  }
  try {
    const result = await executor(input);
    return { success: true, result };
  } catch (err) {
    return { success: false, result: null, error: err.message };
  }
}

const TOOL_EXECUTORS = {
  // HTTP fetch — make API requests
  async http_fetch({ url, method, headers, body, timeout_ms }) {
    if (!url) throw new Error('url is required');
    // Validate URL to prevent SSRF — only allow http/https
    const parsed = new URL(url);
    if (!['http:', 'https:'].includes(parsed.protocol)) {
      throw new Error('Only http/https URLs allowed');
    }

    const opts = {
      method: method || 'GET',
      headers: headers || {},
      signal: AbortSignal.timeout(timeout_ms || 15000),
    };
    if (body) opts.body = typeof body === 'string' ? body : JSON.stringify(body);

    const res = await fetch(url, opts);
    const text = await res.text();
    // Truncate large responses
    const truncated = text.length > 50000 ? text.slice(0, 50000) + '...[truncated]' : text;
    return { status: res.status, body: truncated, ok: res.ok };
  },

  // Read local file
  async read_file({ file_path, max_bytes }) {
    if (!file_path) throw new Error('file_path is required');
    // Security: resolve path, prevent reading outside home or data dirs
    const resolved = path.resolve(file_path);
    const home = process.env.HOME;
    if (!resolved.startsWith(home)) {
      throw new Error('Can only read files under home directory');
    }

    const stat = fs.statSync(resolved);
    const limit = max_bytes || 500000; // 500KB default
    if (stat.size > limit) {
      // Read only the first chunk
      const fd = fs.openSync(resolved, 'r');
      const buf = Buffer.alloc(limit);
      fs.readSync(fd, buf, 0, limit, 0);
      fs.closeSync(fd);
      return buf.toString('utf8') + '...[truncated]';
    }
    return fs.readFileSync(resolved, 'utf8');
  },

  // Shell command — restricted via allowlist, uses execFile (no shell injection)
  async shell_exec({ command, args, timeout_ms }) {
    if (!command) throw new Error('command is required');
    // Allowlist of safe commands
    const ALLOWED = ['ls', 'cat', 'head', 'tail', 'grep', 'find', 'wc', 'date', 'echo', 'curl', 'jq'];
    const cmd = path.basename(command);
    if (!ALLOWED.includes(cmd)) {
      throw new Error(`Command not allowed: ${cmd}. Allowed: ${ALLOWED.join(', ')}`);
    }
    // execFile is used intentionally here (not exec) to prevent shell injection
    const { stdout, stderr } = await execFileAsync(command, args || [], {
      timeout: timeout_ms || 10000,
      maxBuffer: 500000,
    });
    return { stdout: stdout.slice(0, 50000), stderr: stderr.slice(0, 5000) };
  },

  // MCP tool call — call a tool on a connected MCP server
  async mcp_call({ server, tool, arguments: args }) {
    if (!server) throw new Error('server is required');
    if (!tool) throw new Error('tool is required');
    const { callMcpTool } = require('./mcp-client');
    const result = await callMcpTool(server, tool, args || {});
    // Extract text content from MCP response
    if (result && result.content) {
      const texts = result.content
        .filter(c => c.type === 'text')
        .map(c => c.text);
      const combined = texts.join('\n');
      return combined.length > 50000 ? combined.slice(0, 50000) + '...[truncated]' : combined;
    }
    return result;
  },

  // List all available MCP tools across configured servers
  async list_mcp_tools() {
    const { listAllTools } = require('./mcp-client');
    const tools = await listAllTools();
    return tools.map(t => ({
      server: t.server,
      name: t.name,
      description: (t.description || '').slice(0, 200),
    }));
  },
};

/**
 * Get tool definitions to pass to Claude API.
 * These tell Claude what tools are available.
 */
function getToolDefinitions() {
  return [
    {
      name: 'http_fetch',
      description: 'Make an HTTP request to fetch data from an API or web service.',
      input_schema: {
        type: 'object',
        properties: {
          url: { type: 'string', description: 'The URL to fetch' },
          method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'DELETE'], default: 'GET' },
          headers: { type: 'object', description: 'Request headers' },
          body: { type: 'string', description: 'Request body (for POST/PUT)' },
          timeout_ms: { type: 'number', description: 'Timeout in milliseconds', default: 15000 },
        },
        required: ['url'],
      },
    },
    {
      name: 'read_file',
      description: 'Read a local file. Use for importing data exports (ChatGPT, etc.).',
      input_schema: {
        type: 'object',
        properties: {
          file_path: { type: 'string', description: 'Absolute path to the file' },
          max_bytes: { type: 'number', description: 'Max bytes to read', default: 500000 },
        },
        required: ['file_path'],
      },
    },
    {
      name: 'shell_exec',
      description: 'Run a shell command. Only safe commands allowed (ls, cat, grep, curl, etc.).',
      input_schema: {
        type: 'object',
        properties: {
          command: { type: 'string', description: 'Command to run (e.g., curl, grep)' },
          args: { type: 'array', items: { type: 'string' }, description: 'Command arguments' },
          timeout_ms: { type: 'number', default: 10000 },
        },
        required: ['command'],
      },
    },
    {
      name: 'mcp_call',
      description: 'Call a tool on an MCP server. Available servers and tools can be discovered via list_mcp_tools.',
      input_schema: {
        type: 'object',
        properties: {
          server: { type: 'string', description: 'MCP server name (e.g., "slack", "sendgrid")' },
          tool: { type: 'string', description: 'Tool name on the server' },
          arguments: { type: 'object', description: 'Tool arguments' },
        },
        required: ['server', 'tool'],
      },
    },
    {
      name: 'list_mcp_tools',
      description: 'List all available MCP tools across all configured servers.',
      input_schema: { type: 'object', properties: {} },
    },
  ];
}

module.exports = { executeTool, getToolDefinitions, TOOL_EXECUTORS };
