import express from 'express';
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { getUserPaths } from '../services/user-directories.js';
import { addToCodexConfig, removeFromCodexConfig } from '../services/codex-mcp.js';

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 {
    console.log('📋 Listing MCP servers using Claude CLI');

    const userUuid = req.user?.uuid;
    if (!userUuid) {
      return res.status(401).json({ error: 'User authentication required' });
    }

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

    const cliProcess = spawn('claude', ['mcp', 'list'], {
      stdio: ['pipe', 'pipe', 'pipe'],
      env: { ...process.env, CLAUDE_CONFIG_DIR: userPaths.claudeDir }
    });

    let stdout = '';
    let stderr = '';

    cliProcess.stdout.on('data', (data) => {
      stdout += data.toString();
    });

    cliProcess.stderr.on('data', (data) => {
      stderr += data.toString();
    });

    cliProcess.on('close', (code) => {
      if (code === 0) {
        res.json({ success: true, output: stdout, servers: parseClaudeListOutput(stdout) });
      } else {
        console.error('Claude CLI error:', stderr);
        res.status(500).json({ error: 'Claude CLI command failed', details: stderr });
      }
    });

    cliProcess.on('error', (error) => {
      console.error('Error running Claude CLI:', error);
      res.status(500).json({ error: 'Failed to run Claude CLI', details: error.message });
    });
  } catch (error) {
    console.error('Error listing MCP servers via CLI:', error);
    res.status(500).json({ error: 'Failed to list MCP servers', details: error.message });
  }
});

// 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;

    const userUuid = req.user?.uuid;
    if (!userUuid) {
      return res.status(401).json({ error: 'User authentication required' });
    }

    console.log(`➕ Adding MCP server (${scope} scope):`, name);

    const userPaths = getUserPaths(userUuid);

    // For local scope, bypass Claude CLI and directly write to .claude.json.
    // Claude CLI resolves the project key by walking up to the git root, which in
    // our multi-user deployment always resolves to the app root — not the user's
    // actual project directory. We write directly to avoid this mis-keying.
    if (scope === 'local' && projectPath) {
      const claudeJsonPath = path.join(userPaths.claudeDir, '.claude.json');
      let claudeConfig = {};
      try {
        claudeConfig = JSON.parse(await fs.readFile(claudeJsonPath, 'utf-8'));
      } catch { /* start fresh if missing */ }

      // Build the server entry in the same shape Claude CLI would write
      let serverEntry = {};
      if (type === 'http' || type === 'sse') {
        serverEntry = { transport: type, url, ...(Object.keys(headers).length ? { headers } : {}) };
      } else {
        serverEntry = { command, ...(args?.length ? { args } : {}), ...(Object.keys(env).length ? { env } : {}) };
      }

      if (!claudeConfig.projects) claudeConfig.projects = {};
      if (!claudeConfig.projects[projectPath]) claudeConfig.projects[projectPath] = {};
      if (!claudeConfig.projects[projectPath].mcpServers) claudeConfig.projects[projectPath].mcpServers = {};
      claudeConfig.projects[projectPath].mcpServers[name] = serverEntry;

      await fs.writeFile(claudeJsonPath, JSON.stringify(claudeConfig, null, 2), 'utf-8');
      console.log(`[MCP] Wrote local-scoped MCP "${name}" for project ${projectPath}`);
      return res.json({ success: true, message: `MCP server "${name}" added successfully` });
    }

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

    let cliArgs = ['mcp', 'add', '--scope', scope];

    if (type === 'http') {
      cliArgs.push('--transport', 'http', name, url);
      Object.entries(headers).forEach(([key, value]) => { cliArgs.push('--header', `${key}: ${value}`); });
    } else if (type === 'sse') {
      cliArgs.push('--transport', 'sse', name, url);
      Object.entries(headers).forEach(([key, value]) => { cliArgs.push('--header', `${key}: ${value}`); });
    } else {
      cliArgs.push(name);
      Object.entries(env).forEach(([key, value]) => { cliArgs.push('-e', `${key}=${value}`); });
      cliArgs.push(command);
      if (args?.length) cliArgs.push(...args);
    }

    console.log('🔧 Running Claude CLI command:', 'claude', cliArgs.join(' '));

    const cliProcess = spawn('claude', cliArgs, {
      stdio: ['pipe', 'pipe', 'pipe'],
      env: { ...process.env, CLAUDE_CONFIG_DIR: userPaths.claudeDir }
    });

    let stdout = '';
    let stderr = '';
    cliProcess.stdout.on('data', (data) => { stdout += data.toString(); });
    cliProcess.stderr.on('data', (data) => { stderr += data.toString(); });
    cliProcess.on('close', (code) => {
      if (code === 0) {
        res.json({ success: true, output: stdout, message: `MCP server "${name}" added successfully` });
      } else {
        console.error('Claude CLI error:', stderr);
        res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
      }
    });
    cliProcess.on('error', (error) => {
      res.status(500).json({ error: 'Failed to run Claude CLI', details: error.message });
    });
  } catch (error) {
    console.error('Error adding MCP server via CLI:', error);
    res.status(500).json({ error: 'Failed to add MCP server', details: error.message });
  }
});

// POST /api/mcp/cli/add-json - Add MCP server using JSON format
// 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;

    const userUuid = req.user?.uuid;
    if (!userUuid) {
      return res.status(401).json({ error: 'User authentication required' });
    }

    console.log('➕ Adding MCP server using JSON format:', name);

    // 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 });
    }

    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` });
    }

    const userPaths = getUserPaths(userUuid);

    // For local scope, bypass Claude CLI (same reason as cli/add — git root mis-keying).
    if (scope === 'local' && projectPath) {
      const claudeJsonPath = path.join(userPaths.claudeDir, '.claude.json');
      let claudeConfig = {};
      try {
        claudeConfig = JSON.parse(await fs.readFile(claudeJsonPath, 'utf-8'));
      } catch { /* start fresh if missing */ }

      if (!claudeConfig.projects) claudeConfig.projects = {};
      if (!claudeConfig.projects[projectPath]) claudeConfig.projects[projectPath] = {};
      if (!claudeConfig.projects[projectPath].mcpServers) claudeConfig.projects[projectPath].mcpServers = {};
      claudeConfig.projects[projectPath].mcpServers[name] = parsedConfig;

      await fs.writeFile(claudeJsonPath, JSON.stringify(claudeConfig, null, 2), 'utf-8');
      console.log(`[MCP] Wrote local-scoped MCP "${name}" (JSON) for project ${projectPath}`);

      // Sync to Codex config.toml (fire-and-forget)
      const codexConfigPath = path.join(userPaths.codexHomeDir, '.codex', 'config.toml');
      addToCodexConfig(codexConfigPath, { [name]: parsedConfig }).catch(err =>
        console.error('[MCP] Failed to sync to Codex config.toml:', err.message)
      );
      return res.json({ success: true, message: `MCP server "${name}" added successfully via JSON` });
    }

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

    const cliArgs = ['mcp', 'add-json', '--scope', scope, name, JSON.stringify(parsedConfig)];
    console.log('🔧 Running Claude CLI command:', 'claude', cliArgs[0], cliArgs[1], cliArgs[2], cliArgs[3], cliArgs[4]);

    const cliProcess = spawn('claude', cliArgs, {
      stdio: ['pipe', 'pipe', 'pipe'],
      env: { ...process.env, CLAUDE_CONFIG_DIR: userPaths.claudeDir }
    });

    let stdout = '';
    let stderr = '';
    cliProcess.stdout.on('data', (data) => { stdout += data.toString(); });
    cliProcess.stderr.on('data', (data) => { stderr += data.toString(); });
    cliProcess.on('close', (code) => {
      if (code === 0) {
        res.json({ success: true, output: stdout, message: `MCP server "${name}" added successfully via JSON` });
        const codexConfigPath = path.join(userPaths.codexHomeDir, '.codex', 'config.toml');
        addToCodexConfig(codexConfigPath, { [name]: parsedConfig }).catch(err =>
          console.error('[MCP] Failed to sync to Codex config.toml:', err.message)
        );
      } else {
        console.error('Claude CLI error:', stderr);
        res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
      }
    });
    cliProcess.on('error', (error) => {
      res.status(500).json({ error: 'Failed to run Claude CLI', details: error.message });
    });
  } catch (error) {
    console.error('Error adding MCP server via JSON:', error);
    res.status(500).json({ error: 'Failed to add MCP server', details: error.message });
  }
});

// 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, projectPath } = req.query;

    const userUuid = req.user?.uuid;
    if (!userUuid) {
      return res.status(401).json({ error: 'User authentication required' });
    }

    const actualScope = scope || 'user';
    console.log('🗑️ Removing MCP server:', name, 'scope:', actualScope);

    const userPaths = getUserPaths(userUuid);

    // For local scope, bypass Claude CLI and directly edit .claude.json.
    if (actualScope === 'local') {
      if (!projectPath) {
        return res.status(400).json({ error: 'projectPath is required for local-scoped MCP removal' });
      }
      const claudeJsonPath = path.join(userPaths.claudeDir, '.claude.json');
      try {
        const claudeConfig = JSON.parse(await fs.readFile(claudeJsonPath, 'utf-8'));
        const projectMcps = claudeConfig.projects?.[projectPath]?.mcpServers;
        if (projectMcps && name in projectMcps) {
          delete projectMcps[name];
          await fs.writeFile(claudeJsonPath, JSON.stringify(claudeConfig, null, 2), 'utf-8');
        }
      } catch (e) {
        return res.status(500).json({ error: 'Failed to update .claude.json', details: e.message });
      }
      // Sync removal to Codex (fire-and-forget)
      const codexConfigPath = path.join(userPaths.codexHomeDir, '.codex', 'config.toml');
      removeFromCodexConfig(codexConfigPath, name).catch(err =>
        console.error('[MCP] Failed to sync removal to Codex config.toml:', err.message)
      );
      return res.json({ success: true, message: `MCP server "${name}" removed successfully` });
    }

    const { spawn } = await import('child_process');
    const cliArgs = ['mcp', 'remove', '--scope', actualScope, name];
    console.log('🔧 Running Claude CLI command:', 'claude', cliArgs.join(' '));

    const cliProcess = spawn('claude', cliArgs, {
      stdio: ['pipe', 'pipe', 'pipe'],
      env: { ...process.env, CLAUDE_CONFIG_DIR: userPaths.claudeDir }
    });

    let stdout = '';
    let stderr = '';
    cliProcess.stdout.on('data', (data) => { stdout += data.toString(); });
    cliProcess.stderr.on('data', (data) => { stderr += data.toString(); });
    cliProcess.on('close', (code) => {
      if (code === 0) {
        res.json({ success: true, output: stdout, message: `MCP server "${name}" removed successfully` });
        const codexConfigPath = path.join(userPaths.codexHomeDir, '.codex', 'config.toml');
        removeFromCodexConfig(codexConfigPath, name).catch(err =>
          console.error('[MCP] Failed to sync removal to Codex config.toml:', err.message)
        );
      } else {
        console.error('Claude CLI error:', stderr);
        res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
      }
    });
    cliProcess.on('error', (error) => {
      res.status(500).json({ error: 'Failed to run Claude CLI', details: error.message });
    });
  } catch (error) {
    console.error('Error removing MCP server via CLI:', error);
    res.status(500).json({ error: 'Failed to remove MCP server', details: error.message });
  }
});

// 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;

    const userUuid = req.user?.uuid;
    if (!userUuid) {
      return res.status(401).json({ error: 'User authentication required' });
    }

    console.log('📄 Getting MCP server details using Claude CLI:', name);

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

    const cliProcess = spawn('claude', ['mcp', 'get', name], {
      stdio: ['pipe', 'pipe', 'pipe'],
      env: { ...process.env, CLAUDE_CONFIG_DIR: userPaths.claudeDir }
    });

    let stdout = '';
    let stderr = '';

    cliProcess.stdout.on('data', (data) => {
      stdout += data.toString();
    });

    cliProcess.stderr.on('data', (data) => {
      stderr += data.toString();
    });

    cliProcess.on('close', (code) => {
      if (code === 0) {
        res.json({ success: true, output: stdout, server: parseClaudeGetOutput(stdout) });
      } else {
        console.error('Claude CLI error:', stderr);
        res.status(404).json({ error: 'Claude CLI command failed', details: stderr });
      }
    });

    cliProcess.on('error', (error) => {
      console.error('Error running Claude CLI:', error);
      res.status(500).json({ error: 'Failed to run Claude CLI', details: error.message });
    });
  } catch (error) {
    console.error('Error getting MCP server details via CLI:', error);
    res.status(500).json({ error: 'Failed to get MCP server details', details: error.message });
  }
});

// GET /api/mcp/config/read - Read MCP servers directly from Claude config files
router.get('/config/read', async (req, res) => {
  try {
    console.log('📖 Reading MCP servers from Claude config files');

    const userUuid = req.user?.uuid;
    if (!userUuid) {
      return res.status(401).json({
        success: false,
        error: 'User authentication required',
        servers: []
      });
    }

    const userPaths = getUserPaths(userUuid);
    const configPaths = [
      path.join(userPaths.claudeDir, '.claude.json'),
      path.join(userPaths.claudeDir, '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;
        console.log(`✅ Found Claude config at: ${filepath}`);
        break;
      } catch (error) {
        // File doesn't exist or is not valid JSON, try next
        console.log(`ℹ️ Config not found or invalid at: ${filepath}`);
      }
    }

    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) {
      console.log('🔍 Found user-scoped MCP servers:', Object.keys(configData.mcpServers));
      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) — iterate all projects
    if (configData.projects && typeof configData.projects === 'object') {
      for (const [projectPath, projectConfig] of Object.entries(configData.projects)) {
        if (!projectConfig?.mcpServers || typeof projectConfig.mcpServers !== 'object') continue;
        const mcpNames = Object.keys(projectConfig.mcpServers);
        if (mcpNames.length === 0) continue;
        console.log(`🔍 Found local-scoped MCP servers for ${projectPath}:`, mcpNames);
        for (const [name, config] of Object.entries(projectConfig.mcpServers)) {
          const server = {
            id: name,
            name: name,
            type: 'stdio',
            scope: 'local',
            projectPath: projectPath,
            config: {},
            raw: 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);
        }
      }
    }
    
    console.log(`📋 Found ${servers.length} MCP servers in config`);
    
    res.json({ 
      success: true, 
      configPath: configPath,
      servers: servers 
    });
  } catch (error) {
    console.error('Error reading Claude config:', error);
    res.status(500).json({ 
      error: 'Failed to read Claude configuration', 
      details: error.message 
    });
  }
});

// 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
      });
    }
  }
  
  console.log('🔍 Parsed Claude CLI servers:', servers);
  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;