import express from 'express';
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { spawn } from 'child_process';

const router = express.Router();
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Claude CLI command routes

// GET /api/mcp/cli/list - List MCP servers using Claude CLI
router.get('/cli/list', async (req, res) => {
  try {
    // Cloud mode: run claude CLI on user's machine via relay
    if (req.isCloud) {
      if (!req.requireRelay()) return;
      try {
        const result = await req.sendRelay('shell-command', { command: 'claude mcp list' }, 30000);
        const output = result.stdout || result.output || '';
        return res.json({ success: true, output, servers: parseClaudeListOutput(output) });
      } catch (err) {
        return res.status(500).json({ error: 'Claude CLI command failed via relay', details: err.message });
      }
    }

    // Local mode
    const { spawn } = await import('child_process');

    const process = spawn('claude', ['mcp', 'list'], {
      stdio: ['pipe', 'pipe', 'pipe']
    });
    
    let stdout = '';
    let stderr = '';
    
    process.stdout.on('data', (data) => {
      stdout += data.toString();
    });
    
    process.stderr.on('data', (data) => {
      stderr += data.toString();
    });
    
    process.on('close', (code) => {
      if (code === 0) {
        res.json({ success: true, output: stdout, servers: parseClaudeListOutput(stdout) });
      } else {
        // CLI error
        res.status(500).json({ error: 'Claude CLI command failed', details: stderr });
      }
    });
    
    process.on('error', (error) => {
      // CLI run error
      res.status(500).json({ error: 'Failed to run Claude CLI', details: 'An error occurred' });
    });
  } catch (error) {
    // MCP list error
    res.status(500).json({ error: 'Failed to list MCP servers', details: 'An error occurred' });
  }
});

// POST /api/mcp/cli/add - Add MCP server using Claude CLI
router.post('/cli/add', async (req, res) => {
  try {
    const { name, type = 'stdio', command, args = [], url, headers = {}, env = {}, scope = 'user', projectPath } = req.body;

    // Cloud mode: run claude CLI on user's machine via relay
    if (req.isCloud) {
      if (!req.requireRelay()) return;
      try {
        let cliParts = ['claude', 'mcp', 'add', '--scope', scope];
        if (type === 'http') {
          cliParts.push('--transport', 'http', name, url);
          Object.entries(headers).forEach(([key, value]) => { cliParts.push('--header', `${key}: ${value}`); });
        } else if (type === 'sse') {
          cliParts.push('--transport', 'sse', name, url);
          Object.entries(headers).forEach(([key, value]) => { cliParts.push('--header', `${key}: ${value}`); });
        } else {
          cliParts.push(name);
          Object.entries(env).forEach(([key, value]) => { cliParts.push('-e', `${key}=${value}`); });
          cliParts.push(command);
          if (args && args.length > 0) cliParts.push(...args);
        }
        const fullCmd = cliParts.map(a => (typeof a === 'string' && a.includes(' ')) ? `"${a}"` : a).join(' ');
        const payload = { command: fullCmd };
        if (scope === 'local' && projectPath) payload.cwd = projectPath;
        const result = await req.sendRelay('shell-command', payload, 30000);
        return res.json({ success: true, output: result.stdout || result.output || '', message: `MCP server "${name}" added successfully` });
      } catch (err) {
        return res.status(500).json({ error: 'Claude CLI command failed via relay', details: err.message });
      }
    }

    const { spawn } = await import('child_process');
    
    let cliArgs = ['mcp', 'add'];
    
    // Add scope flag
    cliArgs.push('--scope', scope);
    
    if (type === 'http') {
      cliArgs.push('--transport', 'http', name, url);
      // Add headers if provided
      Object.entries(headers).forEach(([key, value]) => {
        cliArgs.push('--header', `${key}: ${value}`);
      });
    } else if (type === 'sse') {
      cliArgs.push('--transport', 'sse', name, url);
      // Add headers if provided
      Object.entries(headers).forEach(([key, value]) => {
        cliArgs.push('--header', `${key}: ${value}`);
      });
    } else {
      // stdio (default): claude mcp add --scope user <name> <command> [args...]
      cliArgs.push(name);
      // Add environment variables
      Object.entries(env).forEach(([key, value]) => {
        cliArgs.push('-e', `${key}=${value}`);
      });
      cliArgs.push(command);
      if (args && args.length > 0) {
        cliArgs.push(...args);
      }
    }
    
    
    // For local scope, we need to run the command in the project directory
    const spawnOptions = {
      stdio: ['pipe', 'pipe', 'pipe']
    };
    
    if (scope === 'local' && projectPath) {
      spawnOptions.cwd = projectPath;
    }
    
    const process = spawn('claude', cliArgs, spawnOptions);
    
    let stdout = '';
    let stderr = '';
    
    process.stdout.on('data', (data) => {
      stdout += data.toString();
    });
    
    process.stderr.on('data', (data) => {
      stderr += data.toString();
    });
    
    process.on('close', (code) => {
      if (code === 0) {
        res.json({ success: true, output: stdout, message: `MCP server "${name}" added successfully` });
      } else {
        // CLI error
        res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
      }
    });
    
    process.on('error', (error) => {
      // CLI run error
      res.status(500).json({ error: 'Failed to run Claude CLI', details: 'An error occurred' });
    });
  } catch (error) {
    // MCP add error
    res.status(500).json({ error: 'Failed to add MCP server', details: 'An error occurred' });
  }
});

// POST /api/mcp/cli/add-json - Add MCP server using JSON format
router.post('/cli/add-json', async (req, res) => {
  try {
    const { name, jsonConfig, scope = 'user', projectPath } = req.body;

    // Validate and parse JSON config
    let parsedConfig;
    try {
      parsedConfig = typeof jsonConfig === 'string' ? JSON.parse(jsonConfig) : jsonConfig;
    } catch (parseError) {
      return res.status(400).json({ 
        error: 'Invalid JSON configuration', 
        details: parseError.message 
      });
    }
    
    // Validate required fields
    if (!parsedConfig.type) {
      return res.status(400).json({ 
        error: 'Invalid configuration', 
        details: 'Missing required field: type' 
      });
    }
    
    if (parsedConfig.type === 'stdio' && !parsedConfig.command) {
      return res.status(400).json({ 
        error: 'Invalid configuration', 
        details: 'stdio type requires a command field' 
      });
    }
    
    if ((parsedConfig.type === 'http' || parsedConfig.type === 'sse') && !parsedConfig.url) {
      return res.status(400).json({ 
        error: 'Invalid configuration', 
        details: `${parsedConfig.type} type requires a url field` 
      });
    }
    
    // Cloud mode: run claude CLI on user's machine via relay
    if (req.isCloud) {
      if (!req.requireRelay()) return;
      try {
        const jsonString = JSON.stringify(parsedConfig);
        const fullCmd = `claude mcp add-json --scope ${scope} ${name} '${jsonString}'`;
        const payload = { command: fullCmd };
        if (scope === 'local' && projectPath) payload.cwd = projectPath;
        const result = await req.sendRelay('shell-command', payload, 30000);
        return res.json({ success: true, output: result.stdout || result.output || '', message: `MCP server "${name}" added successfully via JSON` });
      } catch (err) {
        return res.status(500).json({ error: 'Claude CLI command failed via relay', details: err.message });
      }
    }

    const { spawn } = await import('child_process');

    // Build the command: claude mcp add-json --scope <scope> <name> '<json>'
    const cliArgs = ['mcp', 'add-json', '--scope', scope, name];

    // Add the JSON config as a properly formatted string
    const jsonString = JSON.stringify(parsedConfig);
    cliArgs.push(jsonString);
    
    
    // For local scope, we need to run the command in the project directory
    const spawnOptions = {
      stdio: ['pipe', 'pipe', 'pipe']
    };
    
    if (scope === 'local' && projectPath) {
      spawnOptions.cwd = projectPath;
    }
    
    const process = spawn('claude', cliArgs, spawnOptions);
    
    let stdout = '';
    let stderr = '';
    
    process.stdout.on('data', (data) => {
      stdout += data.toString();
    });
    
    process.stderr.on('data', (data) => {
      stderr += data.toString();
    });
    
    process.on('close', (code) => {
      if (code === 0) {
        res.json({ success: true, output: stdout, message: `MCP server "${name}" added successfully via JSON` });
      } else {
        // CLI error
        res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
      }
    });
    
    process.on('error', (error) => {
      // CLI run error
      res.status(500).json({ error: 'Failed to run Claude CLI', details: 'An error occurred' });
    });
  } catch (error) {
    // MCP JSON add error
    res.status(500).json({ error: 'Failed to add MCP server', details: 'An error occurred' });
  }
});

// DELETE /api/mcp/cli/remove/:name - Remove MCP server using Claude CLI
router.delete('/cli/remove/:name', async (req, res) => {
  try {
    const { name } = req.params;
    const { scope } = req.query;

    // Handle the ID format (remove scope prefix if present)
    let actualName = name;
    let actualScope = scope;

    if (name.includes(':')) {
      const [prefix, serverName] = name.split(':');
      actualName = serverName;
      actualScope = actualScope || prefix;
    }

    // Cloud mode: run claude CLI on user's machine via relay
    if (req.isCloud) {
      if (!req.requireRelay()) return;
      try {
        const scopeFlag = actualScope === 'local' ? '--scope local' : '--scope user';
        const result = await req.sendRelay('shell-command', { command: `claude mcp remove ${scopeFlag} ${actualName}` }, 30000);
        return res.json({ success: true, output: result.stdout || result.output || '', message: `MCP server "${name}" removed successfully` });
      } catch (err) {
        return res.status(500).json({ error: 'Claude CLI command failed via relay', details: err.message });
      }
    }

    const { spawn } = await import('child_process');
    
    // Build command args based on scope
    let cliArgs = ['mcp', 'remove'];
    
    // Add scope flag if it's local scope
    if (actualScope === 'local') {
      cliArgs.push('--scope', 'local');
    } else if (actualScope === 'user' || !actualScope) {
      // User scope is default, but we can be explicit
      cliArgs.push('--scope', 'user');
    }
    
    cliArgs.push(actualName);
    
    
    const process = spawn('claude', cliArgs, {
      stdio: ['pipe', 'pipe', 'pipe']
    });
    
    let stdout = '';
    let stderr = '';
    
    process.stdout.on('data', (data) => {
      stdout += data.toString();
    });
    
    process.stderr.on('data', (data) => {
      stderr += data.toString();
    });
    
    process.on('close', (code) => {
      if (code === 0) {
        res.json({ success: true, output: stdout, message: `MCP server "${name}" removed successfully` });
      } else {
        // CLI error
        res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
      }
    });
    
    process.on('error', (error) => {
      // CLI run error
      res.status(500).json({ error: 'Failed to run Claude CLI', details: 'An error occurred' });
    });
  } catch (error) {
    // MCP remove error
    res.status(500).json({ error: 'Failed to remove MCP server', details: 'An error occurred' });
  }
});

// GET /api/mcp/cli/get/:name - Get MCP server details using Claude CLI
router.get('/cli/get/:name', async (req, res) => {
  try {
    const { name } = req.params;

    // Cloud mode: run claude CLI on user's machine via relay
    if (req.isCloud) {
      if (!req.requireRelay()) return;
      try {
        const result = await req.sendRelay('shell-command', { command: `claude mcp get ${name}` }, 30000);
        const output = result.stdout || result.output || '';
        return res.json({ success: true, output, server: parseClaudeGetOutput(output) });
      } catch (err) {
        return res.status(404).json({ error: 'Claude CLI command failed via relay', details: err.message });
      }
    }

    const { spawn } = await import('child_process');
    
    const process = spawn('claude', ['mcp', 'get', name], {
      stdio: ['pipe', 'pipe', 'pipe']
    });
    
    let stdout = '';
    let stderr = '';
    
    process.stdout.on('data', (data) => {
      stdout += data.toString();
    });
    
    process.stderr.on('data', (data) => {
      stderr += data.toString();
    });
    
    process.on('close', (code) => {
      if (code === 0) {
        res.json({ success: true, output: stdout, server: parseClaudeGetOutput(stdout) });
      } else {
        // CLI error
        res.status(404).json({ error: 'Claude CLI command failed', details: stderr });
      }
    });
    
    process.on('error', (error) => {
      // CLI run error
      res.status(500).json({ error: 'Failed to run Claude CLI', details: 'An error occurred' });
    });
  } catch (error) {
    // MCP CLI details error
    res.status(500).json({ error: 'Failed to get MCP server details', details: 'An error occurred' });
  }
});

// GET /api/mcp/config/read - Read MCP servers directly from Claude config files
router.get('/config/read', async (req, res) => {
  try {
    // Cloud mode: read config from user's machine via relay
    if (req.isCloud) {
      if (!req.requireRelay()) return;
      const configFiles = ['~/.claude.json', '~/.claude/settings.json'];
      let configData = null;
      let configPath = null;

      for (const filePath of configFiles) {
        try {
          const result = await req.sendRelay('file-read', { filePath }, 15000);
          configData = JSON.parse(result.content);
          configPath = filePath;
          break;
        } catch { /* try next */ }
      }

      if (!configData) {
        return res.json({ success: true, configPath: null, servers: [] });
      }

      const servers = [];
      if (configData.mcpServers && typeof configData.mcpServers === 'object') {
        for (const [name, config] of Object.entries(configData.mcpServers)) {
          const server = { id: name, name, type: 'stdio', scope: 'user', config: {}, raw: config };
          if (config.command) {
            server.type = 'stdio';
            server.config = { command: config.command, args: config.args || [], env: config.env || {} };
          } else if (config.url) {
            server.type = config.transport || 'http';
            server.config = { url: config.url, headers: config.headers || {} };
          }
          servers.push(server);
        }
      }
      // Check for project-scoped MCP servers if projectPath is provided
      const cloudProjectPath = req.query.projectPath;
      if (cloudProjectPath && configData.projects && typeof configData.projects === 'object') {
        const projectConfig = configData.projects[cloudProjectPath];
        if (projectConfig?.mcpServers && typeof projectConfig.mcpServers === 'object') {
          for (const [name, config] of Object.entries(projectConfig.mcpServers)) {
            const server = { id: `project:${name}`, name, type: 'stdio', scope: 'project', projectPath: cloudProjectPath, config: {}, raw: config };
            if (config.command) {
              server.type = 'stdio';
              server.config = { command: config.command, args: config.args || [], env: config.env || {} };
            } else if (config.url) {
              server.type = config.transport || 'http';
              server.config = { url: config.url, headers: config.headers || {} };
            }
            servers.push(server);
          }
        }
      }

      return res.json({ success: true, configPath, servers });
    }

    const homeDir = os.homedir();
    const configPaths = [
      path.join(homeDir, '.claude.json'),
      path.join(homeDir, '.claude', 'settings.json')
    ];

    let configData = null;
    let configPath = null;

    // Try to read from either config file
    for (const filepath of configPaths) {
      try {
        const fileContent = await fs.readFile(filepath, 'utf8');
        configData = JSON.parse(fileContent);
        configPath = filepath;
        break;
      } catch (error) {
        // File doesn't exist or is not valid JSON, try next
      }
    }
    
    if (!configData) {
      return res.json({ 
        success: false, 
        message: 'No Claude configuration file found',
        servers: [] 
      });
    }
    
    // Extract MCP servers from the config
    const servers = [];
    
    // Check for user-scoped MCP servers (at root level)
    if (configData.mcpServers && typeof configData.mcpServers === 'object' && Object.keys(configData.mcpServers).length > 0) {
      for (const [name, config] of Object.entries(configData.mcpServers)) {
        const server = {
          id: name,
          name: name,
          type: 'stdio', // Default type
          scope: 'user',  // User scope - available across all projects
          config: {},
          raw: config // Include raw config for full details
        };
        
        // Determine transport type and extract config
        if (config.command) {
          server.type = 'stdio';
          server.config.command = config.command;
          server.config.args = config.args || [];
          server.config.env = config.env || {};
        } else if (config.url) {
          server.type = config.transport || 'http';
          server.config.url = config.url;
          server.config.headers = config.headers || {};
        }
        
        servers.push(server);
      }
    }
    
    // Check for local-scoped MCP servers (project-specific)
    // Use provided projectPath query param, or fall back to cwd
    const currentProjectPath = req.query.projectPath || process.cwd();

    // Check under 'projects' key
    if (configData.projects && configData.projects[currentProjectPath]) {
      const projectConfig = configData.projects[currentProjectPath];
      if (projectConfig.mcpServers && typeof projectConfig.mcpServers === 'object' && Object.keys(projectConfig.mcpServers).length > 0) {
        for (const [name, config] of Object.entries(projectConfig.mcpServers)) {
          const server = {
            id: `local:${name}`,  // Prefix with scope for uniqueness
            name: name,           // Keep original name
            type: 'stdio', // Default type
            scope: 'local',  // Local scope - only for this project
            projectPath: currentProjectPath,
            config: {},
            raw: config // Include raw config for full details
          };
          
          // Determine transport type and extract config
          if (config.command) {
            server.type = 'stdio';
            server.config.command = config.command;
            server.config.args = config.args || [];
            server.config.env = config.env || {};
          } else if (config.url) {
            server.type = config.transport || 'http';
            server.config.url = config.url;
            server.config.headers = config.headers || {};
          }
          
          servers.push(server);
        }
      }
    }
    
    
    res.json({ 
      success: true, 
      configPath: configPath,
      servers: servers 
    });
  } catch (error) {
    res.status(500).json({ 
      error: 'Failed to read Claude configuration', 
      details: 'An error occurred'
    });
  }
});

// Helper functions to parse Claude CLI output
function parseClaudeListOutput(output) {
  const servers = [];
  const lines = output.split('\n').filter(line => line.trim());
  
  for (const line of lines) {
    // Skip the header line
    if (line.includes('Checking MCP server health')) continue;
    
    // Parse lines like "test: test test - ✗ Failed to connect"
    // or "server-name: command or description - ✓ Connected"
    if (line.includes(':')) {
      const colonIndex = line.indexOf(':');
      const name = line.substring(0, colonIndex).trim();
      
      // Skip empty names
      if (!name) continue;
      
      // Extract the rest after the name
      const rest = line.substring(colonIndex + 1).trim();
      
      // Try to extract description and status
      let description = rest;
      let status = 'unknown';
      let type = 'stdio'; // default type
      
      // Check for status indicators
      if (rest.includes('✓') || rest.includes('✗')) {
        const statusMatch = rest.match(/(.*?)\s*-\s*([✓✗].*)$/);
        if (statusMatch) {
          description = statusMatch[1].trim();
          status = statusMatch[2].includes('✓') ? 'connected' : 'failed';
        }
      }
      
      // Try to determine type from description
      if (description.startsWith('http://') || description.startsWith('https://')) {
        type = 'http';
      }
      
      servers.push({
        name,
        type,
        status: status || 'active',
        description
      });
    }
  }
  
  return servers;
}

function parseClaudeGetOutput(output) {
  // Parse the output from 'claude mcp get <name>' command
  // This is a simple parser - might need adjustment based on actual output format
  try {
    // Try to extract JSON if present
    const jsonMatch = output.match(/\{[\s\S]*\}/);
    if (jsonMatch) {
      return JSON.parse(jsonMatch[0]);
    }
    
    // Otherwise, parse as text
    const server = { raw_output: output };
    const lines = output.split('\n');
    
    for (const line of lines) {
      if (line.includes('Name:')) {
        server.name = line.split(':')[1]?.trim();
      } else if (line.includes('Type:')) {
        server.type = line.split(':')[1]?.trim();
      } else if (line.includes('Command:')) {
        server.command = line.split(':')[1]?.trim();
      } else if (line.includes('URL:')) {
        server.url = line.split(':')[1]?.trim();
      }
    }
    
    return server;
  } catch (error) {
    return { raw_output: output, parse_error: error.message };
  }
}

export default router;