import http from 'http';
import { URL } from 'url';
import { readFile } from 'fs/promises';
import { join, extname } from 'path';
import { existsSync } from 'fs';
// Import route handlers
import { handleStatusRoutes } from './routes/status-routes.js';
import { handleCliRoutes, cleanupStaleExecutions } from './routes/cli-routes.js';
import { handleCliSettingsRoutes } from './routes/cli-settings-routes.js';
import { handleCliSessionsRoutes } from './routes/cli-sessions-routes.js';
import { handleAuditRoutes } from './routes/audit-routes.js';
import { handleProviderRoutes } from './routes/provider-routes.js';
import { handleMemoryRoutes } from './routes/memory-routes.js';
import { handleCoreMemoryRoutes } from './routes/core-memory-routes.js';
import { handleUnifiedMemoryRoutes } from './routes/unified-memory-routes.js';
import { handleMcpRoutes } from './routes/mcp-routes.js';
import { handleHooksRoutes } from './routes/hooks-routes.js';
import { handleUnsplashRoutes, handleBackgroundRoutes } from './routes/unsplash-routes.js';
import { handleGraphRoutes } from './routes/graph-routes.js';
import { handleSystemRoutes } from './routes/system-routes.js';
import { handleFilesRoutes } from './routes/files-routes.js';
import { handleSkillsRoutes } from './routes/skills-routes.js';
import { handleSkillHubRoutes } from './routes/skill-hub-routes.js';
import { handleCommandsRoutes } from './routes/commands-routes.js';
import { handleIssueRoutes } from './routes/issue-routes.js';
import { handleQueueSchedulerRoutes } from './routes/queue-routes.js';
import { handleDiscoveryRoutes } from './routes/discovery-routes.js';
import { handleRulesRoutes } from './routes/rules-routes.js';
import { handleSessionRoutes } from './routes/session-routes.js';
import { handleCcwRoutes } from './routes/ccw-routes.js';
import { handleClaudeRoutes } from './routes/claude-routes.js';
import { handleHelpRoutes } from './routes/help-routes.js';
import { handleLiteLLMRoutes } from './routes/litellm-routes.js';
import { handleLiteLLMApiRoutes } from './routes/litellm-api-routes.js';
import { handleNavStatusRoutes } from './routes/nav-status-routes.js';
import { handleAuthRoutes } from './routes/auth-routes.js';
import { handleLoopRoutes } from './routes/loop-routes.js';
import { handleLoopV2Routes, initializeCliToolsCache } from './routes/loop-v2-routes.js';
import { handleTestLoopRoutes } from './routes/test-loop-routes.js';
import { handleTaskRoutes } from './routes/task-routes.js';
import { handleDashboardRoutes } from './routes/dashboard-routes.js';
import { handleOrchestratorRoutes } from './routes/orchestrator-routes.js';
import { handleConfigRoutes } from './routes/config-routes.js';
import { handleTeamRoutes } from './routes/team-routes.js';
import { handleNotificationRoutes } from './routes/notification-routes.js';
import { handleAnalysisRoutes } from './routes/analysis-routes.js';
import { handleSpecRoutes } from './routes/spec-routes.js';
import { handleDeepWikiRoutes } from './routes/deepwiki-routes.js';

// Import WebSocket handling
import { handleWebSocketUpgrade, broadcastToClients, extractSessionIdFromPath } from './websocket.js';

import { getTokenManager } from './auth/token-manager.js';
import { authMiddleware, isLocalhostRequest, isWildcardHost, setAuthCookie } from './auth/middleware.js';
import { getCorsOrigin } from './cors.js';
import { csrfValidation } from './auth/csrf-middleware.js';
import { getCsrfTokenManager } from './auth/csrf-manager.js';
import { randomBytes } from 'crypto';
import { getFrontendStaticDir } from '../utils/react-frontend.js';

// Import health check service
import { getHealthCheckService } from './services/health-check-service.js';
import { getCliSessionShareManager } from './services/cli-session-share.js';
import { getCliSessionManager } from './services/cli-session-manager.js';
import { QueueSchedulerService } from './services/queue-scheduler-service.js';

// Import status check functions for warmup
import { getCliToolsStatus } from '../tools/cli-executor.js';

import type { ServerConfig } from '../types/config.js';
import type { PostRequestHandler } from './routes/types.js';

interface ServerOptions {
  port?: number;
  initialPath?: string;
  host?: string;
  open?: boolean;
  reactPort?: number;
}

type PostHandler = PostRequestHandler;

/**
 * MIME type mapping for static files
 */
const MIME_TYPES: Record<string, string> = {
  '.html': 'text/html',
  '.js': 'application/javascript',
  '.css': 'text/css',
  '.json': 'application/json',
  '.png': 'image/png',
  '.jpg': 'image/jpeg',
  '.jpeg': 'image/jpeg',
  '.gif': 'image/gif',
  '.svg': 'image/svg+xml',
  '.ico': 'image/x-icon',
  '.woff': 'font/woff',
  '.woff2': 'font/woff2',
  '.ttf': 'font/ttf',
  '.eot': 'application/vnd.ms-fontobject',
};

/**
 * Serve static file from frontend dist directory
 */
async function serveStaticFile(
  filePath: string,
  res: http.ServerResponse
): Promise<boolean> {
  try {
    if (!existsSync(filePath)) {
      return false;
    }

    const content = await readFile(filePath);
    const ext = extname(filePath);
    const mimeType = MIME_TYPES[ext] || 'application/octet-stream';

    // Determine cache strategy based on file type
    const fileName = filePath.split('/').pop() || '';
    const isIndexHtml = filePath.endsWith('index.html');
    const isAssetFile = fileName.startsWith('index-') && (ext === '.js' || ext === '.css');

    // For index.html: use no-cache to prevent stale content issues
    if (isIndexHtml) {
      res.writeHead(200, {
        'Content-Type': mimeType,
        'Cache-Control': 'no-cache',
      });
      res.end(content);
      return true;
    }

    // For assets (JS/CSS with hash in filenames), use long-term cache
    if (isAssetFile) {
      res.writeHead(200, {
        'Content-Type': mimeType,
        'Cache-Control': 'public, max-age=31536000, immutable',
      });
      res.end(content);
      return true;
    }

    // For other files (fallback to index.html for SPA), use no-cache
    res.writeHead(200, {
      'Content-Type': mimeType,
      'Cache-Control': 'no-cache',
    });
    res.end(content);
    return true;
  } catch (error) {
    console.error(`[Static] Error serving ${filePath}:`, error);
    return false;
  }
}

/**
 * Handle POST request with JSON body
 */
function handlePostRequest(req: http.IncomingMessage, res: http.ServerResponse, handler: PostHandler): void {
  const cachedParsed = (req as any).body;
  const cachedRawBody = (req as any).__ccwRawBody;

  const handleBody = async (parsed: unknown) => {
    try {
      const result = await handler(parsed);

      const isObjectResult = typeof result === 'object' && result !== null;
      const errorValue = isObjectResult && 'error' in result ? (result as { error?: unknown }).error : undefined;
      const statusValue = isObjectResult && 'status' in result ? (result as { status?: unknown }).status : undefined;

      if (typeof errorValue === 'string' && errorValue.length > 0) {
        const status = typeof statusValue === 'number' ? statusValue : 500;
        res.writeHead(status, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ error: errorValue }));
        return;
      }

      // Support custom success status codes (e.g., 201 Created)
      const successStatus = typeof statusValue === 'number' && statusValue >= 200 && statusValue < 300
        ? statusValue
        : 200;
      res.writeHead(successStatus, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify(result));
    } catch (error: unknown) {
      const message = error instanceof Error ? error.message : String(error);
      res.writeHead(500, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: message }));
    }
  };

  if (cachedParsed !== undefined) {
    void handleBody(cachedParsed);
    return;
  }

  if (typeof cachedRawBody === 'string') {
    try {
      const trimmed = cachedRawBody.trim();
      void handleBody(trimmed.length === 0 ? {} : JSON.parse(cachedRawBody));
    } catch (error: unknown) {
      const message = error instanceof Error ? error.message : String(error);
      res.writeHead(500, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: message }));
    }
    return;
  }

  let body = '';
  req.on('data', (chunk: Buffer) => { body += chunk.toString(); });
  req.on('end', async () => {
    try {
      (req as any).__ccwRawBody = body;
      const trimmed = body.trim();
      const parsed = trimmed.length === 0 ? {} : JSON.parse(body);
      (req as any).body = parsed;
      await handleBody(parsed);
    } catch (error: unknown) {
      const message = error instanceof Error ? error.message : String(error);
      res.writeHead(500, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: message }));
    }
  });
}

function getHeaderValue(header: string | string[] | undefined): string | null {
  if (!header) return null;
  if (Array.isArray(header)) return header[0] ?? null;
  return header;
}

function parseCookieHeader(cookieHeader: string | null | undefined): Record<string, string> {
  if (!cookieHeader) return {};

  const cookies: Record<string, string> = {};
  for (const part of cookieHeader.split(';')) {
    const [rawName, ...rawValueParts] = part.trim().split('=');
    if (!rawName) continue;
    const rawValue = rawValueParts.join('=');
    try {
      cookies[rawName] = decodeURIComponent(rawValue);
    } catch {
      cookies[rawName] = rawValue;
    }
  }
  return cookies;
}

function appendSetCookie(res: http.ServerResponse, cookie: string): void {
  const existing = res.getHeader('Set-Cookie');
  if (!existing) {
    res.setHeader('Set-Cookie', cookie);
    return;
  }

  if (Array.isArray(existing)) {
    res.setHeader('Set-Cookie', [...existing, cookie]);
    return;
  }

  res.setHeader('Set-Cookie', [String(existing), cookie]);
}

function getOrCreateSessionId(req: http.IncomingMessage, res: http.ServerResponse): string {
  const cookies = parseCookieHeader(getHeaderValue(req.headers.cookie));
  const existing = cookies.ccw_session_id;
  if (existing) return existing;

  const created = randomBytes(16).toString('hex');
  const attributes = [
    `ccw_session_id=${encodeURIComponent(created)}`,
    'Path=/',
    'HttpOnly',
    'SameSite=Strict',
    `Max-Age=${24 * 60 * 60}`,
  ];
  appendSetCookie(res, attributes.join('; '));
  return created;
}

function setCsrfCookie(res: http.ServerResponse, token: string, maxAgeSeconds: number): void {
  const attributes = [
    `XSRF-TOKEN=${encodeURIComponent(token)}`,
    'Path=/',
    // Note: XSRF-TOKEN must be readable by JavaScript for CSRF protection to work
    // The token is also sent via X-CSRF-Token header, so not having HttpOnly is safe
    'SameSite=Strict',
    `Max-Age=${maxAgeSeconds}`,
  ];
  appendSetCookie(res, attributes.join('; '));
}

/**
 * Warmup function to pre-populate caches on server startup
 * This runs asynchronously and non-blocking after the server starts
 */
async function warmupCaches(initialPath: string): Promise<void> {
  console.log('[WARMUP] Starting cache warmup...');
  const startTime = Date.now();

  // Run all warmup tasks in parallel for faster startup
  const warmupTasks = [
    // Warmup CLI tools status cache
    (async () => {
      const taskStart = Date.now();
      try {
        const cliStatus = await getCliToolsStatus();
        const availableCount = Object.values(cliStatus).filter(s => s.available).length;
        const totalCount = Object.keys(cliStatus).length;
        console.log(`[WARMUP] CLI tools status: ${availableCount}/${totalCount} available (${Date.now() - taskStart}ms)`);
      } catch (err) {
        console.warn(`[WARMUP] CLI tools status check failed: ${(err as Error).message}`);
      }
    })()
  ];

  await Promise.allSettled(warmupTasks);
  console.log(`[WARMUP] Cache warmup complete (${Date.now() - startTime}ms total)`);
}

/**
 * Read request body as text for proxy requests
 * @param req - HTTP request object
 * @returns Promise that resolves to body text
 */
async function readRequestBody(req: http.IncomingMessage): Promise<string> {
  return new Promise((resolve, reject) => {
    let body = '';
    req.on('data', (chunk) => { body += chunk; });
    req.on('end', () => { resolve(body); });
    req.on('error', reject);
  });
}

/**
 * Create and start the dashboard server
 * @param {Object} options - Server options
 * @param {number} options.port - Port to listen on (default: 3456)
 * @param {string} options.initialPath - Initial project path
 * @returns {Promise<http.Server>}
 */
export async function startServer(options: ServerOptions = {}): Promise<http.Server> {
  let serverPort = options.port ?? 3456;
  const initialPath = options.initialPath || process.cwd();
  const host = options.host ?? '127.0.0.1';
  const reactPort = options.reactPort || serverPort + 1;

  console.log(`[Server] React proxy configured: /* -> http://localhost:${reactPort}`);

  const tokenManager = getTokenManager();
  const secretKey = tokenManager.getSecretKey();
  tokenManager.getOrCreateAuthToken();
  const unauthenticatedPaths = new Set<string>(['/api/auth/token', '/api/csrf-token', '/api/hook', '/api/test/ask-question', '/api/a2ui/answer']);
  const cliSessionShareManager = getCliSessionShareManager();

  // Initialize Queue Scheduler Service (needs broadcastToClients and cliSessionManager)
  const cliSessionManager = getCliSessionManager(initialPath);
  const queueSchedulerService = new QueueSchedulerService(broadcastToClients, cliSessionManager);

  const server = http.createServer(async (req, res) => {
    const url = new URL(req.url ?? '/', `http://localhost:${serverPort}`);
    const pathname = url.pathname;

    // CORS headers for API requests
    const originHeader = Array.isArray(req.headers.origin) ? req.headers.origin[0] : req.headers.origin;
    res.setHeader('Access-Control-Allow-Origin', getCorsOrigin(originHeader, serverPort));
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-CSRF-Token');
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.setHeader('Access-Control-Expose-Headers', 'X-CSRF-Token');
    res.setHeader('Vary', 'Origin');

    if (req.method === 'OPTIONS') {
      res.writeHead(200);
      res.end();
      return;
    }

    try {
      // Debug log for API requests
      if (pathname.startsWith('/api/')) {
        console.log(`[API] ${req.method} ${pathname}`);
      }

      // Route context for all handlers
      const routeContext = {
        pathname,
        url,
        req,
        res,
        initialPath,
        handlePostRequest,
        broadcastToClients,
        extractSessionIdFromPath,
        server
      };

      // Token acquisition endpoint (localhost-only, or any interface when bound to 0.0.0.0)
      if (pathname === '/api/auth/token') {
        // Allow from any interface when server is bound to 0.0.0.0 or ::
        const allowAllInterfaces = isWildcardHost(host);
        if (!isLocalhostRequest(req, allowAllInterfaces)) {
          res.writeHead(403, { 'Content-Type': 'application/json; charset=utf-8' });
          res.end(JSON.stringify({ error: 'Forbidden' }));
          return;
        }

        const tokenResult = tokenManager.getOrCreateAuthToken();
        setAuthCookie(res, tokenResult.token, tokenResult.expiresAt);
        res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
        res.end(JSON.stringify({ token: tokenResult.token, expiresAt: tokenResult.expiresAt.toISOString() }));
        return;
      }

      // Authentication middleware for all API routes
      if (pathname.startsWith('/api/')) {
        let shareBypass = false;
        const shareToken = url.searchParams.get('shareToken');
        if (shareToken) {
          const match = pathname.match(/^\/api\/cli-sessions\/([^/]+)\/(buffer|stream)$/);
          if (match?.[1]) {
            const sessionKey = decodeURIComponent(match[1]);
            const validated = cliSessionShareManager.validateToken(shareToken, sessionKey);
            if (validated && (validated.mode === 'read' || validated.mode === 'write')) {
              (req as any).__cliSessionShareProjectRoot = validated.projectRoot;
              shareBypass = true;
            }
          }
        }

        if (!shareBypass) {
          const ok = authMiddleware({ pathname, req, res, tokenManager, secretKey, unauthenticatedPaths });
          if (!ok) return;
        }
      }

      // CSRF validation middleware for state-changing API routes
      if (pathname.startsWith('/api/')) {
        const ok = await csrfValidation({ pathname, req, res });
        if (!ok) return;
      }

      // Try each route handler in order
      // Order matters: more specific routes should come before general ones

      // Test endpoint for ask_question tool (temporary for E2E testing)
      if (pathname === '/api/test/ask-question' && req.method === 'POST') {
        const { executeTool } = await import('../tools/index.js');

        // Get question params from request body if provided, or use default
        let questionParams = {
          question: {
            id: 'test-question-' + Date.now(),
            type: 'confirm',
            title: 'Test Question',
            message: 'This is a test of the ask_question tool integration',
            description: 'Click Confirm or Cancel to complete the test'
          },
          timeout: 30000
        };

        if (req.headers['content-type']?.includes('application/json')) {
          try {
            const chunks: Buffer[] = [];
            for await (const chunk of req) {
              chunks.push(chunk);
            }
            const body = JSON.parse(Buffer.concat(chunks).toString());
            if (body.question) {
              questionParams.question = { ...questionParams.question, ...body.question };
            }
            if (body.timeout) {
              questionParams.timeout = body.timeout;
            }
          } catch (e) {
            // Use defaults if parsing fails
          }
        }

        const result = await executeTool('ask_question', questionParams);
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(result));
        return;
      }

      // Auth routes (/api/csrf-token)
      if (await handleAuthRoutes(routeContext)) return;

      // Status routes (/api/status/*) - Aggregated endpoint for faster loading
      if (pathname.startsWith('/api/status/')) {
        if (await handleStatusRoutes(routeContext)) return;
      }

      // Navigation status routes (/api/nav-status) - Aggregated badge counts
      if (pathname === '/api/nav-status') {
        if (await handleNavStatusRoutes(routeContext)) return;
      }

      // Dashboard routes (/api/dashboard/*, /api/workflow-status-counts)
      if (pathname.startsWith('/api/dashboard/') || pathname === '/api/workflow-status-counts') {
        if (await handleDashboardRoutes(routeContext)) return;
      }

      // Analysis routes (/api/analysis/*)
      if (pathname.startsWith('/api/analysis')) {
        if (await handleAnalysisRoutes(routeContext)) return;
      }

      // CLI sessions (PTY) routes (/api/cli-sessions/*) - independent from /api/cli/*
      if (pathname.startsWith('/api/cli-sessions')) {
        if (await handleCliSessionsRoutes(routeContext)) return;
      }

      // Audit routes (/api/audit/*)
      if (pathname.startsWith('/api/audit')) {
        if (await handleAuditRoutes(routeContext)) return;
      }

      // CLI routes (/api/cli/*)
      if (pathname.startsWith('/api/cli/')) {
        // CLI Settings routes first (more specific path /api/cli/settings/*)
        if (await handleCliSettingsRoutes(routeContext)) return;
        if (await handleCliRoutes(routeContext)) return;
      }

      // Provider routes (/api/providers/*)
      if (pathname.startsWith('/api/providers')) {
        if (await handleProviderRoutes(routeContext)) return;
      }

      // Claude CLAUDE.md routes (/api/memory/claude/*) and Language routes (/api/language/*)
      if (pathname.startsWith('/api/memory/claude/') || pathname.startsWith('/api/language/')) {
        if (await handleClaudeRoutes(routeContext)) return;
      }

      // Memory routes (/api/memory and /api/memory/*)
      if (pathname === '/api/memory' || pathname.startsWith('/api/memory/')) {
        if (await handleMemoryRoutes(routeContext)) return;
      }

      // Core Memory routes (/api/core-memory/*)
      if (pathname.startsWith('/api/core-memory/')) {
        if (await handleCoreMemoryRoutes(routeContext)) return;
      }

      // Unified Memory routes (/api/unified-memory/*)
      if (pathname.startsWith('/api/unified-memory/')) {
        if (await handleUnifiedMemoryRoutes(routeContext)) return;
      }


      // MCP routes (/api/mcp*, /api/codex-mcp*)
      if (pathname.startsWith('/api/mcp') || pathname.startsWith('/api/codex-mcp')) {
        if (await handleMcpRoutes(routeContext)) return;
      }

      // DeepWiki routes (/api/deepwiki/*)
      if (pathname.startsWith('/api/deepwiki')) {
        if (await handleDeepWikiRoutes(routeContext)) return;
      }

      // Hooks routes (/api/hooks, /api/hook)
      if (pathname.startsWith('/api/hook')) {
        if (await handleHooksRoutes(routeContext)) return;
      }

      // Background image upload/serve routes (/api/background/*)
      if (pathname.startsWith('/api/background/')) {
        if (await handleBackgroundRoutes(routeContext)) return;
      }

      // Unsplash proxy routes (/api/unsplash/*)
      if (pathname.startsWith('/api/unsplash/')) {
        if (await handleUnsplashRoutes(routeContext)) return;
      }

      // LiteLLM routes (/api/litellm/*)
      if (pathname.startsWith('/api/litellm/')) {
        if (await handleLiteLLMRoutes(routeContext)) return;
      }

      // LiteLLM API routes (/api/litellm-api/*)
      if (pathname.startsWith('/api/litellm-api/')) {
        if (await handleLiteLLMApiRoutes(routeContext)) return;
      }

      // Graph routes (/api/graph/*)
      if (pathname.startsWith('/api/graph/')) {
        if (await handleGraphRoutes(routeContext)) return;
      }

      // Spec routes (/api/specs/*)
      if (pathname.startsWith('/api/specs/')) {
        if (await handleSpecRoutes(routeContext)) return;
      }

      // CCW routes (/api/ccw and /api/ccw/*)
      if (pathname.startsWith('/api/ccw')) {
        if (await handleCcwRoutes(routeContext)) return;
      }

      // Orchestrator routes (/api/orchestrator/*)
      if (pathname.startsWith('/api/orchestrator/')) {
        if (await handleOrchestratorRoutes(routeContext)) return;
      }

      // Config routes (/api/config/*)
      if (pathname.startsWith('/api/config/')) {
        if (await handleConfigRoutes(routeContext)) return;
      }

      // Loop V2 routes (/api/loops/v2/*) - must be checked before v1
      if (pathname.startsWith('/api/loops/v2')) {
        if (await handleLoopV2Routes(routeContext)) return;
      }

      // Loop V1 routes (/api/loops/*) - backward compatibility
      if (pathname.startsWith('/api/loops')) {
        if (await handleLoopRoutes(routeContext)) return;
      }

      // Team routes (/api/teams*)
      if (pathname.startsWith('/api/teams')) {
        if (await handleTeamRoutes(routeContext)) return;
      }

      // Remote notification routes (/api/notifications/remote/*)
      if (pathname.startsWith('/api/notifications/remote')) {
        if (await handleNotificationRoutes(req, res, pathname)) return;
      }

      // Task routes (/api/tasks)
      if (pathname.startsWith('/api/tasks')) {
        if (await handleTaskRoutes(routeContext)) return;
      }

      // Test loop routes (/api/test/loop*)
      if (pathname.startsWith('/api/test/loop')) {
        if (await handleTestLoopRoutes(routeContext)) return;
      }

      // Skills routes (/api/skills*)
      if (pathname.startsWith('/api/skills')) {
        if (await handleSkillsRoutes(routeContext)) return;
      }

      // Skill Hub routes (/api/skill-hub*)
      if (pathname.startsWith('/api/skill-hub')) {
        if (await handleSkillHubRoutes(routeContext)) return;
      }

      // Commands routes (/api/commands*)
      if (pathname.startsWith('/api/commands')) {
        if (await handleCommandsRoutes(routeContext)) return;
      }

      // Queue Scheduler routes (/api/queue/execute, /api/queue/scheduler/*)
      if (pathname === '/api/queue/execute' || pathname.startsWith('/api/queue/scheduler')) {
        if (await handleQueueSchedulerRoutes(routeContext, queueSchedulerService)) return;
      }

      // Queue routes (/api/queue*) - top-level queue API (issue-based)
      if (pathname.startsWith('/api/queue')) {
        if (await handleIssueRoutes(routeContext)) return;
      }

      // Issue routes (/api/issues*)
      if (pathname.startsWith('/api/issues')) {
        if (await handleIssueRoutes(routeContext)) return;
      }

      // Discovery routes (/api/discoveries*)
      if (pathname.startsWith('/api/discoveries')) {
        if (await handleDiscoveryRoutes(routeContext)) return;
      }

      // Rules routes (/api/rules*)
      if (pathname.startsWith('/api/rules')) {
        if (await handleRulesRoutes(routeContext)) return;
      }

      // Help routes (/api/help/*)
      if (pathname.startsWith('/api/help/')) {
        if (await handleHelpRoutes(routeContext)) return;
      }

      // Session routes (/api/session-detail, /api/update-task-status, /api/bulk-update-task-status)
      if (pathname.includes('session') || pathname.includes('task-status')) {
        if (await handleSessionRoutes(routeContext)) return;
      }

      // Files routes (/api/files, /api/file, /api/file-content, /api/update-claude-md, /api/explorer/*)
      if (pathname === '/api/files' || pathname === '/api/file' ||
          pathname === '/api/file-content' || pathname === '/api/update-claude-md' ||
          pathname.startsWith('/api/explorer/')) {
        if (await handleFilesRoutes(routeContext)) return;
      }

      // System routes (data, health, version, paths, shutdown, notify, storage, dialog, a2ui answer broker, system settings, project-tech)
      if (pathname === '/api/data' || pathname === '/api/health' ||
          pathname === '/api/version-check' || pathname === '/api/shutdown' ||
          pathname === '/api/recent-paths' || pathname === '/api/switch-path' ||
          pathname === '/api/remove-recent-path' || pathname === '/api/system/notify' ||
          pathname === '/api/system/settings' || pathname === '/api/system/hooks/install-recommended' ||
          pathname === '/api/a2ui/answer' || pathname === '/api/project-tech/stats' ||
          pathname.startsWith('/api/storage/') || pathname.startsWith('/api/dialog/')) {
        if (await handleSystemRoutes(routeContext)) return;
      }

      // Handle favicon.ico (return empty response to prevent 404)
      if (pathname === '/favicon.ico') {
        res.writeHead(204);
        res.end();
        return;
      }

      // Backward compatibility: redirect /react/* to /* (strip /react prefix)
      if (pathname === '/react' || pathname.startsWith('/react/')) {
        const newPath = pathname === '/react' ? '/' : pathname.slice('/react'.length);
        res.writeHead(301, { 'Location': `${newPath}${url.search}` });
        res.end();
        return;
      }

      // React frontend proxy - forward all non-API requests to Vite dev server or serve static files
      {
        const frontendStaticDir = getFrontendStaticDir();

        // If we have a static build, serve files directly
        if (frontendStaticDir) {
          let filePath = join(frontendStaticDir, pathname === '/' ? 'index.html' : pathname.slice(1));

          // Try to serve the file
          const served = await serveStaticFile(filePath, res);

          // If file not found, serve index.html for SPA routing
          if (!served && !pathname.startsWith('/api/')) {
            filePath = join(frontendStaticDir, 'index.html');
            const indexServed = await serveStaticFile(filePath, res);

            if (!indexServed) {
              res.writeHead(404, { 'Content-Type': 'text/plain' });
              res.end('Frontend not found');
            }
          }
          return;
        }

        // Otherwise, proxy to Vite dev server
        const reactUrl = `http://127.0.0.1:${reactPort}${pathname}${url.search}`;

        try {
          // Convert headers to plain object for fetch
          const proxyHeaders: Record<string, string> = {};
          for (const [key, value] of Object.entries(req.headers)) {
            if (typeof value === 'string') {
              proxyHeaders[key] = value;
            } else if (Array.isArray(value)) {
              proxyHeaders[key] = value.join(', ');
            }
          }
          proxyHeaders['host'] = `127.0.0.1:${reactPort}`;

          const reactResponse = await fetch(reactUrl, {
            method: req.method,
            headers: proxyHeaders,
            body: req.method !== 'GET' && req.method !== 'HEAD' ? await readRequestBody(req) : undefined,
          });

          const contentType = reactResponse.headers.get('content-type') || 'text/html';
          const body = await reactResponse.text();

          res.writeHead(reactResponse.status, {
            'Content-Type': contentType,
            'Cache-Control': 'no-cache',
          });
          res.end(body);
          return;
        } catch (err) {
          console.error(`[React Proxy] Failed to proxy to ${reactUrl}:`, (err as Error).message);
          res.writeHead(502, { 'Content-Type': 'text/plain' });
          res.end(`Bad Gateway: React frontend not available at ${reactUrl}\nError: ${(err as Error).message}`);
          return;
        }
      }

    } catch (error: unknown) {
      console.error('Server error:', error);
      const message = error instanceof Error ? error.message : String(error);
      res.writeHead(500, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: message }));
    }
  });

  // Handle WebSocket upgrade requests
  server.on('upgrade', (req, socket, head) => {
    if (req.url === '/ws') {
      handleWebSocketUpgrade(req, socket, head);
    } else {
      socket.destroy();
    }
  });

  return new Promise((resolve, reject) => {
    server.listen(serverPort, host, () => {
      const addr = server.address();
      if (addr && typeof addr === 'object') {
        serverPort = addr.port;
      }

      console.log(`Dashboard server running at http://${host}:${serverPort}`);
      console.log(`WebSocket endpoint available at ws://${host}:${serverPort}/ws`);
      console.log(`Hook endpoint available at POST http://${host}:${serverPort}/api/hook`);

      // Initialize CLI tools cache for Loop V2 routes
      initializeCliToolsCache();

      // Start periodic cleanup of stale CLI executions (every 2 minutes)
      const CLEANUP_INTERVAL_MS = 2 * 60 * 1000;
      const cleanupInterval = setInterval(cleanupStaleExecutions, CLEANUP_INTERVAL_MS);
      server.on('close', () => {
        clearInterval(cleanupInterval);
        console.log('[Server] Stopped CLI execution cleanup interval');
      });

      // Start health check service for all enabled providers
      try {
        const healthCheckService = getHealthCheckService();
        healthCheckService.startAllHealthChecks(initialPath);

        // Graceful shutdown: stop health checks when server closes
        server.on('close', () => {
          console.log('[Server] Shutting down health check service...');
          healthCheckService.stopAllHealthChecks();
        });
      } catch (err) {
        console.warn('[Server] Failed to start health check service:', err);
      }

      // Start cache warmup asynchronously (non-blocking)
      // Uses setImmediate to not delay server startup response
      const warmupDisabled = ['1', 'true', 'yes'].includes(
        (process.env.CCW_DISABLE_WARMUP ?? '').trim().toLowerCase(),
      );
      if (!warmupDisabled) {
        setImmediate(() => {
          warmupCaches(initialPath).catch((err) => {
            console.warn('[WARMUP] Cache warmup failed:', err);
          });
        });
      }

      resolve(server);
    });
    server.on('error', reject);
  });
}
