// @ts-nocheck
/**
 * MCP Routes Module
 * Handles all MCP-related API endpoints
 */
import type { IncomingMessage, ServerResponse } from 'http';
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { homedir } from 'os';

// Claude config file path
const CLAUDE_CONFIG_PATH = join(homedir(), '.claude.json');

export interface RouteContext {
  pathname: string;
  url: URL;
  req: IncomingMessage;
  res: ServerResponse;
  initialPath: string;
  handlePostRequest: (req: IncomingMessage, res: ServerResponse, handler: (body: unknown) => Promise<any>) => void;
  broadcastToClients: (data: unknown) => void;
}

// ========================================
// Helper Functions
// ========================================

/**
 * Get enterprise managed MCP path (platform-specific)
 */
function getEnterpriseMcpPath(): string {
  const platform = process.platform;
  if (platform === 'darwin') {
    return '/Library/Application Support/ClaudeCode/managed-mcp.json';
  } else if (platform === 'win32') {
    return 'C:\\Program Files\\ClaudeCode\\managed-mcp.json';
  } else {
    // Linux and WSL
    return '/etc/claude-code/managed-mcp.json';
  }
}

/**
 * Safely read and parse JSON file
 */
function safeReadJson(filePath) {
  try {
    if (!existsSync(filePath)) return null;
    const content = readFileSync(filePath, 'utf8');
    return JSON.parse(content);
  } catch {
    return null;
  }
}

/**
 * Get MCP servers from a JSON file (expects mcpServers key at top level)
 * @param {string} filePath
 * @returns {Object} mcpServers object or empty object
 */
function getMcpServersFromFile(filePath) {
  const config = safeReadJson(filePath);
  if (!config) return {};
  return config.mcpServers || {};
}

/**
 * Get MCP configuration from multiple sources (per official Claude Code docs):
 *
 * Priority (highest to lowest):
 * 1. Enterprise managed-mcp.json (cannot be overridden)
 * 2. Local scope (project-specific private in ~/.claude.json)
 * 3. Project scope (.mcp.json in project root)
 * 4. User scope (mcpServers in ~/.claude.json)
 *
 * Note: ~/.claude/settings.json is for MCP PERMISSIONS, NOT definitions!
 *
 * @returns {Object}
 */
function getMcpConfig() {
  try {
    const result = {
      projects: {},
      userServers: {},        // User-level servers from ~/.claude.json mcpServers
      enterpriseServers: {},  // Enterprise managed servers (highest priority)
      configSources: []       // Track where configs came from for debugging
    };

    // 1. Read Enterprise managed MCP servers (highest priority)
    const enterprisePath = getEnterpriseMcpPath();
    if (existsSync(enterprisePath)) {
      const enterpriseConfig = safeReadJson(enterprisePath);
      if (enterpriseConfig?.mcpServers) {
        result.enterpriseServers = enterpriseConfig.mcpServers;
        result.configSources.push({ type: 'enterprise', path: enterprisePath, count: Object.keys(enterpriseConfig.mcpServers).length });
      }
    }

    // 2. Read from ~/.claude.json
    if (existsSync(CLAUDE_CONFIG_PATH)) {
      const claudeConfig = safeReadJson(CLAUDE_CONFIG_PATH);
      if (claudeConfig) {
        // 2a. User-level mcpServers (top-level mcpServers key)
        if (claudeConfig.mcpServers) {
          result.userServers = claudeConfig.mcpServers;
          result.configSources.push({ type: 'user', path: CLAUDE_CONFIG_PATH, count: Object.keys(claudeConfig.mcpServers).length });
        }

        // 2b. Project-specific configurations (projects[path].mcpServers)
        if (claudeConfig.projects) {
          result.projects = claudeConfig.projects;
        }
      }
    }

    // 3. For each known project, check for .mcp.json (project-level config)
    const projectPaths = Object.keys(result.projects);
    for (const projectPath of projectPaths) {
      const mcpJsonPath = join(projectPath, '.mcp.json');
      if (existsSync(mcpJsonPath)) {
        const mcpJsonConfig = safeReadJson(mcpJsonPath);
        if (mcpJsonConfig?.mcpServers) {
          // Merge .mcp.json servers into project config
          // Project's .mcp.json has lower priority than ~/.claude.json projects[path].mcpServers
          const existingServers = result.projects[projectPath]?.mcpServers || {};
          result.projects[projectPath] = {
            ...result.projects[projectPath],
            mcpServers: {
              ...mcpJsonConfig.mcpServers,  // .mcp.json (lower priority)
              ...existingServers             // ~/.claude.json projects[path] (higher priority)
            },
            mcpJsonPath: mcpJsonPath  // Track source for debugging
          };
          result.configSources.push({ type: 'project-mcp-json', path: mcpJsonPath, count: Object.keys(mcpJsonConfig.mcpServers).length });
        }
      }
    }

    // Build globalServers by merging user and enterprise servers
    // Enterprise servers override user servers
    result.globalServers = {
      ...result.userServers,
      ...result.enterpriseServers
    };

    return result;
  } catch (error: unknown) {
    console.error('Error reading MCP config:', error);
    return { projects: {}, globalServers: {}, userServers: {}, enterpriseServers: {}, configSources: [], error: (error as Error).message };
  }
}

/**
 * Normalize project path for .claude.json (Windows backslash format)
 * @param {string} path
 * @returns {string}
 */
function normalizeProjectPathForConfig(path) {
  // Convert forward slashes to backslashes for Windows .claude.json format
  let normalized = path.replace(/\//g, '\\');

  // Handle /d/path format -> D:\path
  if (normalized.match(/^\\[a-zA-Z]\\/)) {
    normalized = normalized.charAt(1).toUpperCase() + ':' + normalized.slice(2);
  }

  return normalized;
}

/**
 * Toggle MCP server enabled/disabled
 * @param {string} projectPath
 * @param {string} serverName
 * @param {boolean} enable
 * @returns {Object}
 */
function toggleMcpServerEnabled(projectPath, serverName, enable) {
  try {
    if (!existsSync(CLAUDE_CONFIG_PATH)) {
      return { error: '.claude.json not found' };
    }

    const content = readFileSync(CLAUDE_CONFIG_PATH, 'utf8');
    const config = JSON.parse(content);

    const normalizedPath = normalizeProjectPathForConfig(projectPath);

    if (!config.projects || !config.projects[normalizedPath]) {
      return { error: `Project not found: ${normalizedPath}` };
    }

    const projectConfig = config.projects[normalizedPath];

    // Ensure disabledMcpServers array exists
    if (!projectConfig.disabledMcpServers) {
      projectConfig.disabledMcpServers = [];
    }

    if (enable) {
      // Remove from disabled list
      projectConfig.disabledMcpServers = projectConfig.disabledMcpServers.filter(s => s !== serverName);
    } else {
      // Add to disabled list if not already there
      if (!projectConfig.disabledMcpServers.includes(serverName)) {
        projectConfig.disabledMcpServers.push(serverName);
      }
    }

    // Write back to file
    writeFileSync(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');

    return {
      success: true,
      serverName,
      enabled: enable,
      disabledMcpServers: projectConfig.disabledMcpServers
    };
  } catch (error: unknown) {
    console.error('Error toggling MCP server:', error);
    return { error: (error as Error).message };
  }
}

/**
 * Add MCP server to project
 * @param {string} projectPath
 * @param {string} serverName
 * @param {Object} serverConfig
 * @returns {Object}
 */
function addMcpServerToProject(projectPath, serverName, serverConfig) {
  try {
    if (!existsSync(CLAUDE_CONFIG_PATH)) {
      return { error: '.claude.json not found' };
    }

    const content = readFileSync(CLAUDE_CONFIG_PATH, 'utf8');
    const config = JSON.parse(content);

    const normalizedPath = normalizeProjectPathForConfig(projectPath);

    // Create project entry if it doesn't exist
    if (!config.projects) {
      config.projects = {};
    }

    if (!config.projects[normalizedPath]) {
      config.projects[normalizedPath] = {
        allowedTools: [],
        mcpContextUris: [],
        mcpServers: {},
        enabledMcpjsonServers: [],
        disabledMcpjsonServers: [],
        hasTrustDialogAccepted: false,
        projectOnboardingSeenCount: 0,
        hasClaudeMdExternalIncludesApproved: false,
        hasClaudeMdExternalIncludesWarningShown: false
      };
    }

    const projectConfig = config.projects[normalizedPath];

    // Ensure mcpServers exists
    if (!projectConfig.mcpServers) {
      projectConfig.mcpServers = {};
    }

    // Add the server
    projectConfig.mcpServers[serverName] = serverConfig;

    // Write back to file
    writeFileSync(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');

    return {
      success: true,
      serverName,
      serverConfig
    };
  } catch (error: unknown) {
    console.error('Error adding MCP server:', error);
    return { error: (error as Error).message };
  }
}

/**
 * Remove MCP server from project
 * @param {string} projectPath
 * @param {string} serverName
 * @returns {Object}
 */
function removeMcpServerFromProject(projectPath, serverName) {
  try {
    if (!existsSync(CLAUDE_CONFIG_PATH)) {
      return { error: '.claude.json not found' };
    }

    const content = readFileSync(CLAUDE_CONFIG_PATH, 'utf8');
    const config = JSON.parse(content);

    const normalizedPath = normalizeProjectPathForConfig(projectPath);

    if (!config.projects || !config.projects[normalizedPath]) {
      return { error: `Project not found: ${normalizedPath}` };
    }

    const projectConfig = config.projects[normalizedPath];

    if (!projectConfig.mcpServers || !projectConfig.mcpServers[serverName]) {
      return { error: `Server not found: ${serverName}` };
    }

    // Remove the server
    delete projectConfig.mcpServers[serverName];

    // Also remove from disabled list if present
    if (projectConfig.disabledMcpServers) {
      projectConfig.disabledMcpServers = projectConfig.disabledMcpServers.filter(s => s !== serverName);
    }

    // Write back to file
    writeFileSync(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');

    return {
      success: true,
      serverName,
      removed: true
    };
  } catch (error: unknown) {
    console.error('Error removing MCP server:', error);
    return { error: (error as Error).message };
  }
}

/**
 * Add MCP server to global/user scope (top-level mcpServers in ~/.claude.json)
 * @param {string} serverName
 * @param {Object} serverConfig
 * @returns {Object}
 */
function addGlobalMcpServer(serverName, serverConfig) {
  try {
    if (!existsSync(CLAUDE_CONFIG_PATH)) {
      return { error: '.claude.json not found' };
    }

    const content = readFileSync(CLAUDE_CONFIG_PATH, 'utf8');
    const config = JSON.parse(content);

    // Ensure top-level mcpServers exists
    if (!config.mcpServers) {
      config.mcpServers = {};
    }

    // Add the server to top-level mcpServers
    config.mcpServers[serverName] = serverConfig;

    // Write back to file
    writeFileSync(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');

    return {
      success: true,
      serverName,
      serverConfig,
      scope: 'global'
    };
  } catch (error: unknown) {
    console.error('Error adding global MCP server:', error);
    return { error: (error as Error).message };
  }
}

/**
 * Remove MCP server from global/user scope (top-level mcpServers)
 * @param {string} serverName
 * @returns {Object}
 */
function removeGlobalMcpServer(serverName) {
  try {
    if (!existsSync(CLAUDE_CONFIG_PATH)) {
      return { error: '.claude.json not found' };
    }

    const content = readFileSync(CLAUDE_CONFIG_PATH, 'utf8');
    const config = JSON.parse(content);

    if (!config.mcpServers || !config.mcpServers[serverName]) {
      return { error: `Global server not found: ${serverName}` };
    }

    // Remove the server from top-level mcpServers
    delete config.mcpServers[serverName];

    // Write back to file
    writeFileSync(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');

    return {
      success: true,
      serverName,
      removed: true,
      scope: 'global'
    };
  } catch (error: unknown) {
    console.error('Error removing global MCP server:', error);
    return { error: (error as Error).message };
  }
}

/**
 * Read settings file safely
 * @param {string} filePath
 * @returns {Object}
 */
function readSettingsFile(filePath) {
  try {
    if (!existsSync(filePath)) {
      return {};
    }
    const content = readFileSync(filePath, 'utf8');
    return JSON.parse(content);
  } catch (error: unknown) {
    console.error(`Error reading settings file ${filePath}:`, error);
    return {};
  }
}

/**
 * Write settings file safely
 * @param {string} filePath
 * @param {Object} settings
 */
function writeSettingsFile(filePath, settings) {
  const dirPath = dirname(filePath);
  // Ensure directory exists
  if (!existsSync(dirPath)) {
    mkdirSync(dirPath, { recursive: true });
  }
  writeFileSync(filePath, JSON.stringify(settings, null, 2), 'utf8');
}

/**
 * Get project settings path
 * @param {string} projectPath
 * @returns {string}
 */
function getProjectSettingsPath(projectPath) {
  const normalizedPath = projectPath.replace(/\//g, '\\').replace(/^\\([a-zA-Z])\\/, '$1:\\');
  return join(normalizedPath, '.claude', 'settings.json');
}

// ========================================
// Route Handlers
// ========================================

/**
 * Handle MCP routes
 * @returns true if route was handled, false otherwise
 */
export async function handleMcpRoutes(ctx: RouteContext): Promise<boolean> {
  const { pathname, url, req, res, initialPath, handlePostRequest, broadcastToClients } = ctx;

  // API: Get MCP configuration
  if (pathname === '/api/mcp-config') {
    const mcpData = getMcpConfig();
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify(mcpData));
    return true;
  }

  // API: Toggle MCP server enabled/disabled
  if (pathname === '/api/mcp-toggle' && req.method === 'POST') {
    handlePostRequest(req, res, async (body) => {
      const { projectPath, serverName, enable } = body;
      if (!projectPath || !serverName) {
        return { error: 'projectPath and serverName are required', status: 400 };
      }
      return toggleMcpServerEnabled(projectPath, serverName, enable);
    });
    return true;
  }

  // API: Copy MCP server to project
  if (pathname === '/api/mcp-copy-server' && req.method === 'POST') {
    handlePostRequest(req, res, async (body) => {
      const { projectPath, serverName, serverConfig } = body;
      if (!projectPath || !serverName || !serverConfig) {
        return { error: 'projectPath, serverName, and serverConfig are required', status: 400 };
      }
      return addMcpServerToProject(projectPath, serverName, serverConfig);
    });
    return true;
  }

  // API: Install CCW MCP server to project
  if (pathname === '/api/mcp-install-ccw' && req.method === 'POST') {
    handlePostRequest(req, res, async (body) => {
      const { projectPath } = body;
      if (!projectPath) {
        return { error: 'projectPath is required', status: 400 };
      }

      // Generate CCW MCP server config
      const ccwMcpConfig = {
        command: "ccw-mcp",
        args: []
      };

      // Use existing addMcpServerToProject to install CCW MCP
      return addMcpServerToProject(projectPath, 'ccw-mcp', ccwMcpConfig);
    });
    return true;
  }

  // API: Remove MCP server from project
  if (pathname === '/api/mcp-remove-server' && req.method === 'POST') {
    handlePostRequest(req, res, async (body) => {
      const { projectPath, serverName } = body;
      if (!projectPath || !serverName) {
        return { error: 'projectPath and serverName are required', status: 400 };
      }
      return removeMcpServerFromProject(projectPath, serverName);
    });
    return true;
  }

  // API: Add MCP server to global scope (top-level mcpServers in ~/.claude.json)
  if (pathname === '/api/mcp-add-global-server' && req.method === 'POST') {
    handlePostRequest(req, res, async (body) => {
      const { serverName, serverConfig } = body;
      if (!serverName || !serverConfig) {
        return { error: 'serverName and serverConfig are required', status: 400 };
      }
      return addGlobalMcpServer(serverName, serverConfig);
    });
    return true;
  }

  // API: Remove MCP server from global scope
  if (pathname === '/api/mcp-remove-global-server' && req.method === 'POST') {
    handlePostRequest(req, res, async (body) => {
      const { serverName } = body;
      if (!serverName) {
        return { error: 'serverName is required', status: 400 };
      }
      return removeGlobalMcpServer(serverName);
    });
    return true;
  }

  return false;
}
