import { timingSafeEqual } from 'node:crypto';
import { once } from 'node:events';
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
import { createMcpHandler } from '@modelcontextprotocol/server';
import { localhostHostValidation, localhostOriginValidation, toNodeHandler } from '@modelcontextprotocol/node';
import { ExecutorBridge } from './executor-bridge.js';
import { ExecutionContinuityScheduler } from './execution-continuity.js';
import { loadExecutorRuntimeConfig } from './executor-auth.js';
import { loadHttpRuntimeConfig } from './runtime-config.js';
import { createDefaultService, createOrchestratorMcpServer } from './server.js';
import { startRetention } from './retention.js';

function writeJson(res: ServerResponse, status: number, body: unknown, headers?: Record<string, string>): void {
  res.writeHead(status, { 'Content-Type': 'application/json', ...headers });
  res.end(JSON.stringify(body));
}

function bearerMatches(header: string | undefined, expected: string): boolean {
  if (!header?.startsWith('Bearer ')) return false;
  const actual = Buffer.from(header.slice('Bearer '.length), 'utf8');
  const wanted = Buffer.from(expected, 'utf8');
  return actual.length === wanted.length && timingSafeEqual(actual, wanted);
}

const service = createDefaultService();
const retention = await startRetention(service);
const executorConfig = await loadExecutorRuntimeConfig();
const httpConfig = await loadHttpRuntimeConfig();
const bridge = new ExecutorBridge(service, executorConfig);
await bridge.start();
const continuity = new ExecutionContinuityScheduler(service, bridge);
continuity.start();

const mcpHandler = createMcpHandler(() => createOrchestratorMcpServer(service, bridge, continuity), {
  legacy: 'stateless',
  responseMode: 'json',
});
const nodeHandler = toNodeHandler(mcpHandler);
const validateHost = localhostHostValidation();
const validateOrigin = localhostOriginValidation();

const server = createServer((req: IncomingMessage, res: ServerResponse) => {
  if (!validateHost(req, res) || !validateOrigin(req, res)) return;
  const pathname = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`).pathname;

  if (pathname === '/healthz') {
    if (req.method !== 'GET') {
      writeJson(res, 405, { error: 'method_not_allowed' }, { Allow: 'GET' });
      return;
    }
    writeJson(res, 200, {
      ok: true,
      service: '@platform-modules/chatgpt-orchestrator-mcp',
      executorBridge: bridge.address,
      executor: bridge.status,
    });
    return;
  }

  if (pathname !== '/mcp') {
    writeJson(res, 404, { error: 'not_found' });
    return;
  }

  if (!bearerMatches(req.headers.authorization, httpConfig.token)) {
    writeJson(res, 401, { error: 'unauthorized' }, { 'WWW-Authenticate': 'Bearer' });
    return;
  }

  if (!req.method) {
    writeJson(res, 400, { error: 'missing_method' });
    return;
  }
  void nodeHandler(req as Parameters<typeof nodeHandler>[0], res);
});

server.listen(httpConfig.port, httpConfig.host);
await once(server, 'listening');
console.error(`chatgpt-orchestrator HTTP MCP listening on http://${httpConfig.host}:${httpConfig.port}/mcp`);
console.error(`chatgpt-orchestrator executor bridge listening on ${bridge.address}`);
console.error(`MCP bearer token file: ${httpConfig.tokenPath}`);
console.error(`Executor token file: ${executorConfig.tokenPath}`);

let closing = false;
async function shutdown(signal: string): Promise<void> {
  if (closing) return;
  closing = true;
  retention.close();
  continuity.stop();
  console.error(`chatgpt-orchestrator shutting down (${signal})`);
  await new Promise<void>((resolve) => server.close(() => resolve()));
  await bridge.close();
  await mcpHandler.close();
}

process.once('SIGINT', () => { void shutdown('SIGINT'); });
process.once('SIGTERM', () => { void shutdown('SIGTERM'); });
