import { randomUUID } from 'node:crypto';
import express from 'express';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { createServer } from '../server.js';
import { processManager } from '../lib/processManager.js';
import { browserManager } from '../tools/browser/index.js';
import { dockerManager } from '../tools/docker/index.js';
import { logger } from '../lib/logger.js';
import { config } from '../config.js';

const app = express();
app.use(express.json());

if (config.httpToken) {
  app.use((req, res, next) => {
    const auth = req.header('authorization');
    if (auth === `Bearer ${config.httpToken}`) {
      next();
      return;
    }
    res.status(401).json({ error: 'Unauthorized' });
  });
} else if (config.httpHost !== '127.0.0.1' && config.httpHost !== 'localhost') {
  logger.error(
    `Refusing to bind to non-localhost host "${config.httpHost}" without MCP_HTTP_TOKEN set. ` +
      'Set MCP_HTTP_TOKEN, or bind to 127.0.0.1/localhost for unauthenticated local use.',
  );
  process.exit(1);
}

const transports = new Map<string, StreamableHTTPServerTransport>();

app.post('/mcp', async (req, res) => {
  try {
    const sessionId = req.header('mcp-session-id');
    let transport = sessionId ? transports.get(sessionId) : undefined;

    if (!transport) {
      const server = createServer();
      transport = new StreamableHTTPServerTransport({
        sessionIdGenerator: () => randomUUID(),
        onsessioninitialized: (id) => {
          transports.set(id, transport!);
          logger.info(`http session initialized: ${id}`);
        },
        onsessionclosed: (id) => {
          transports.delete(id);
          logger.info(`http session closed: ${id}`);
        },
      });
      await server.connect(transport);
    }

    await transport.handleRequest(req, res, req.body);
  } catch (error) {
    logger.error('error handling POST /mcp', { error: error instanceof Error ? error.message : error });
    if (!res.headersSent) res.status(500).json({ error: 'Internal server error' });
  }
});

app.get('/mcp', async (req, res) => {
  const sessionId = req.header('mcp-session-id');
  const transport = sessionId ? transports.get(sessionId) : undefined;
  if (!transport) {
    res.status(400).send('Invalid or missing mcp-session-id');
    return;
  }
  await transport.handleRequest(req, res);
});

app.delete('/mcp', async (req, res) => {
  const sessionId = req.header('mcp-session-id');
  const transport = sessionId ? transports.get(sessionId) : undefined;
  if (!transport) {
    res.status(400).send('Invalid or missing mcp-session-id');
    return;
  }
  await transport.handleRequest(req, res);
});

app.get('/healthz', (_req, res) => {
  res.json({ status: 'ok', workspaceRoot: config.workspaceRoot });
});

const httpServer = app.listen(config.httpPort, config.httpHost, () => {
  logger.info(`web-dev-mcp HTTP server listening on http://${config.httpHost}:${config.httpPort}/mcp`);
});

async function shutdown(signal: string): Promise<void> {
  logger.info(`received ${signal}, shutting down`);
  processManager.stopAll();
  await browserManager.close();
  await dockerManager.cleanupAll();
  httpServer.close(() => process.exit(0));
}

process.on('SIGINT', () => void shutdown('SIGINT'));
process.on('SIGTERM', () => void shutdown('SIGTERM'));
