import { createServer } from 'http';
import { Server as MCPServer } from '@modelcontextprotocol/sdk/server/index.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, SetLevelRequestSchema, PingRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
import { createLogger } from '../lib/logger.js';
const logger = createLogger('proxy-server');
export class ProxyServer {
    httpServer = null;
    mcpServer = null;
    transport = null;
    options;
    constructor(options) {
        this.options = options;
    }
    async start() {
        const { host, port, client, sessionName, bearerToken, instructions } = this.options;
        logger.info(`Starting proxy server on ${host}:${port} for session ${sessionName}`);
        this.mcpServer = new MCPServer({
            name: `mcpc-proxy${sessionName}`,
            version: 'yolo',
        }, {
            capabilities: {
                tools: {},
                resources: {},
                prompts: {},
                logging: {},
            },
            ...(instructions && { instructions }),
        });
        this.registerHandlers(client);
        this.httpServer = createServer((req, res) => {
            this.handleRequest(req, res, bearerToken).catch((error) => {
                logger.error('Error handling request:', error);
                if (!res.headersSent) {
                    res.writeHead(500, { 'Content-Type': 'application/json' });
                    res.end(JSON.stringify({ error: 'Internal server error' }));
                }
            });
        });
        await new Promise((resolve, reject) => {
            this.httpServer.listen(port, host, () => {
                logger.info(`Proxy server listening on http://${host}:${port}`);
                resolve();
            });
            this.httpServer.on('error', (error) => {
                logger.error('HTTP server error:', error);
                reject(error);
            });
        });
    }
    async handleRequest(req, res, bearerToken) {
        const { method, url } = req;
        logger.debug(`Proxy request: ${method} ${url}`);
        if (url === '/health' && method === 'GET') {
            res.writeHead(200, { 'Content-Type': 'application/json' });
            res.end(JSON.stringify({ status: 'ok' }));
            return;
        }
        if (bearerToken) {
            const authHeader = req.headers.authorization;
            if (!authHeader || !authHeader.startsWith('Bearer ')) {
                logger.debug('Missing or invalid Authorization header');
                res.writeHead(401, { 'Content-Type': 'application/json' });
                res.end(JSON.stringify({ error: 'Unauthorized: Bearer token required' }));
                return;
            }
            const providedToken = authHeader.slice(7);
            if (providedToken !== bearerToken) {
                logger.debug('Invalid bearer token');
                res.writeHead(403, { 'Content-Type': 'application/json' });
                res.end(JSON.stringify({ error: 'Forbidden: Invalid bearer token' }));
                return;
            }
        }
        if (method === 'POST') {
            this.transport = new StreamableHTTPServerTransport({});
            await this.mcpServer.connect(this.transport);
            await this.transport.handleRequest(req, res);
            return;
        }
        if (method === 'GET') {
            this.transport = new StreamableHTTPServerTransport({});
            await this.mcpServer.connect(this.transport);
            await this.transport.handleRequest(req, res);
            return;
        }
        if (method === 'DELETE') {
            res.writeHead(200, { 'Content-Type': 'application/json' });
            res.end(JSON.stringify({ status: 'session terminated' }));
            return;
        }
        res.writeHead(405, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ error: 'Method not allowed' }));
    }
    registerHandlers(client) {
        this.mcpServer.setRequestHandler(PingRequestSchema, async () => {
            await client.ping();
            return {};
        });
        this.mcpServer.setRequestHandler(ListToolsRequestSchema, async (request) => {
            return await client.listTools(request.params?.cursor);
        });
        this.mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
            return await client.callTool(request.params.name, request.params.arguments);
        });
        this.mcpServer.setRequestHandler(ListResourcesRequestSchema, async (request) => {
            return await client.listResources(request.params?.cursor);
        });
        this.mcpServer.setRequestHandler(ListResourceTemplatesRequestSchema, async (request) => {
            return await client.listResourceTemplates(request.params?.cursor);
        });
        this.mcpServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
            return await client.readResource(request.params.uri);
        });
        this.mcpServer.setRequestHandler(ListPromptsRequestSchema, async (request) => {
            return await client.listPrompts(request.params?.cursor);
        });
        this.mcpServer.setRequestHandler(GetPromptRequestSchema, async (request) => {
            return await client.getPrompt(request.params.name, request.params.arguments);
        });
        this.mcpServer.setRequestHandler(SetLevelRequestSchema, async (request) => {
            await client.setLoggingLevel(request.params.level);
            return {};
        });
    }
    async stop() {
        logger.info('Stopping proxy server...');
        if (this.transport) {
            try {
                await this.transport.close();
            }
            catch (error) {
                logger.warn('Error closing transport:', error);
            }
            this.transport = null;
        }
        if (this.mcpServer) {
            try {
                await this.mcpServer.close();
            }
            catch (error) {
                logger.warn('Error closing MCP server:', error);
            }
            this.mcpServer = null;
        }
        if (this.httpServer) {
            await new Promise((resolve) => {
                this.httpServer.close(() => {
                    logger.debug('HTTP server closed');
                    resolve();
                });
            });
            this.httpServer = null;
        }
        logger.info('Proxy server stopped');
    }
    getAddress() {
        return `http://${this.options.host}:${this.options.port}`;
    }
}
//# sourceMappingURL=proxy-server.js.map