#!/usr/bin/env node

/**
 * URL Builder MCP Server with HTTP and stdio transport support
 * 
 * MCP server that provides URL building tools following project-specific rules
 * Supports both stdio and HTTP/SSE transports for different deployment scenarios
 */

import express from 'express';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { buildBookingUrl, validateUrlParams } from './urlBuilder.js';
import { UrlParams } from './types.js';

class UrlBuilderServer {
  private server: Server;

  constructor() {
    this.server = new Server({
      name: 'url-builder-mcp',
      version: '1.0.0',
    });

    this.setupToolHandlers();
    this.setupErrorHandling();
  }

  private setupErrorHandling(): void {
    this.server.onerror = (error) => console.error('[MCP Error]', error);
    process.on('SIGINT', async () => {
      await this.server.close();
      process.exit(0);
    });
    process.on('SIGTERM', async () => {
      await this.server.close();
      process.exit(0);
    });
  }

  private setupToolHandlers(): void {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'build_booking_url',
          description: 'Build external booking URL with query parameters',
          inputSchema: {
            type: 'object',
            properties: {
              checkIn: {
                type: 'string',
                description: 'Check-in date in YYYY-MM-DD format',
                pattern: '^\\d{4}-\\d{2}-\\d{2}$'
              },
              checkOut: {
                type: 'string',
                description: 'Check-out date in YYYY-MM-DD format',
                pattern: '^\\d{4}-\\d{2}-\\d{2}$'
              },
              guestName: {
                type: 'string',
                description: 'Guest name (required)'
              },
              guestPhone: {
                type: 'string',
                description: 'Guest phone (required)'
              },
              adults: {
                type: 'number',
                description: 'Number of adults (optional, default: 2)',
                minimum: 1,
                maximum: 20
              },
              children: {
                type: 'number',
                description: 'Number of children (optional, default: 0)',
                minimum: 0,
                maximum: 10
              },
              roomType: {
                type: 'string',
                description: 'Room type name (optional, e.g., "garden-deluxe")'
              },
              guestEmail: {
                type: 'string',
                description: 'Guest email (optional)'
              }
            },
            required: ['checkIn', 'checkOut', 'guestName', 'guestPhone']
          }
        },
        {
          name: 'validate_url_params',
          description: 'Validate URL parameters without building URLs',
          inputSchema: {
            type: 'object',
            properties: {
              checkIn: {
                type: 'string',
                description: 'Check-in date in YYYY-MM-DD format',
                pattern: '^\\d{4}-\\d{2}-\\d{2}$'
              },
              checkOut: {
                type: 'string',
                description: 'Check-out date in YYYY-MM-DD format',
                pattern: '^\\d{4}-\\d{2}-\\d{2}$'
              },
              adults: {
                type: 'number',
                description: 'Number of adults (optional)',
                minimum: 1,
                maximum: 20
              },
              children: {
                type: 'number',
                description: 'Number of children (optional)',
                minimum: 0,
                maximum: 10
              },
              roomType: {
                type: 'string',
                description: 'Room type name (optional, e.g., "garden-deluxe")'
              },
              guestName: {
                type: 'string',
                description: 'Guest name (optional)'
              },
              guestEmail: {
                type: 'string',
                description: 'Guest email (optional)'
              },
              guestPhone: {
                type: 'string',
                description: 'Guest phone (optional)'
              }
            },
            required: ['checkIn', 'checkOut']
          }
        }
      ]
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;

      try {
        switch (name) {
          case 'build_booking_url': {
            const params = args as unknown as UrlParams;
            const url = buildBookingUrl(params);
            return {
              content: [
                {
                  type: 'text',
                  text: `Booking URL: ${url}`
                }
              ]
            };
          }

          case 'validate_url_params': {
            const params = args as unknown as UrlParams;
            const validation = validateUrlParams(params);
            return {
              content: [
                {
                  type: 'text',
                  text: validation.valid 
                    ? 'Parameters are valid' 
                    : `Validation errors: ${validation.errors.join(', ')}`
                }
              ]
            };
          }

          default:
            throw new Error(`Unknown tool: ${name}`);
        }
      } catch (error) {
        return {
          content: [
            {
              type: 'text',
              text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`
            }
          ],
          isError: true
        };
      }
    });
  }

  async runStdio(): Promise<void> {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('URL Builder MCP server running on stdio');
  }

  async runHttp(port: number = 8000): Promise<void> {
    const app = express();
    let servers: Server[] = [];

    // Health check endpoint for Coolify
    app.get('/health', (req, res) => {
      res.status(200).json({ 
        status: 'healthy', 
        service: 'url-builder-mcp',
        version: '1.0.0',
        timestamp: new Date().toISOString()
      });
    });

    // SSE endpoint for MCP connections
    app.get('/sse', async (req, res) => {
      console.error('Got new SSE connection');
      const transport = new SSEServerTransport('/message', res);
      const server = new Server({
        name: 'url-builder-mcp',
        version: '1.0.0',
      });

      // Copy tool handlers to the new server instance
      this.copyHandlersTo(server);
      
      servers.push(server);
      server.onclose = () => {
        console.error('SSE connection closed');
        servers = servers.filter((s) => s !== server);
      };
      
      await server.connect(transport);
    });

    // Message endpoint for SSE transport
    app.post('/message', async (req, res) => {
      const sessionId = req.query.sessionId as string;
      const transport = servers
        .map((s) => s.transport)
        .find((t) => (t as any).sessionId === sessionId) as SSEServerTransport;
      
      if (!transport) {
        res.status(404).send('Session not found');
        return;
      }
      
      await transport.handlePostMessage(req, res);
    });

    app.listen(port, '0.0.0.0', () => {
      console.error(`URL Builder MCP server running on http://0.0.0.0:${port}`);
      console.error(`Health check available at http://0.0.0.0:${port}/health`);
      console.error(`SSE endpoint available at http://0.0.0.0:${port}/sse`);
    });
  }

  private copyHandlersTo(server: Server): void {
    // Copy the tool handlers to the new server instance
    server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'build_booking_url',
          description: 'Build external booking URL with query parameters',
          inputSchema: {
            type: 'object',
            properties: {
              checkIn: { type: 'string', description: 'Check-in date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$' },
              checkOut: { type: 'string', description: 'Check-out date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$' },
              guestName: { type: 'string', description: 'Guest name (required)' },
              guestPhone: { type: 'string', description: 'Guest phone (required)' },
              adults: { type: 'number', description: 'Number of adults (optional, default: 2)', minimum: 1, maximum: 20 },
              children: { type: 'number', description: 'Number of children (optional, default: 0)', minimum: 0, maximum: 10 },
              roomType: { type: 'string', description: 'Room type name (optional, e.g., "garden-deluxe")' },
              guestEmail: { type: 'string', description: 'Guest email (optional)' }
            },
            required: ['checkIn', 'checkOut', 'guestName', 'guestPhone']
          }
        },
        {
          name: 'validate_url_params',
          description: 'Validate URL parameters without building URLs',
          inputSchema: {
            type: 'object',
            properties: {
              checkIn: { type: 'string', description: 'Check-in date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$' },
              checkOut: { type: 'string', description: 'Check-out date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$' },
              adults: { type: 'number', description: 'Number of adults (optional)', minimum: 1, maximum: 20 },
              children: { type: 'number', description: 'Number of children (optional)', minimum: 0, maximum: 10 },
              roomType: { type: 'string', description: 'Room type name (optional, e.g., "garden-deluxe")' },
              guestName: { type: 'string', description: 'Guest name (optional)' },
              guestEmail: { type: 'string', description: 'Guest email (optional)' },
              guestPhone: { type: 'string', description: 'Guest phone (optional)' }
            },
            required: ['checkIn', 'checkOut']
          }
        }
      ]
    }));

    server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;
      try {
        switch (name) {
          case 'build_booking_url': {
            const params = args as unknown as UrlParams;
            const url = buildBookingUrl(params);
            return { content: [{ type: 'text', text: `Booking URL: ${url}` }] };
          }
          case 'validate_url_params': {
            const params = args as unknown as UrlParams;
            const validation = validateUrlParams(params);
            return {
              content: [{
                type: 'text',
                text: validation.valid ? 'Parameters are valid' : `Validation errors: ${validation.errors.join(', ')}`
              }]
            };
          }
          default:
            throw new Error(`Unknown tool: ${name}`);
        }
      } catch (error) {
        return {
          content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }],
          isError: true
        };
      }
    });
  }
}

// Parse command line arguments
const args = process.argv.slice(2);
const transport = args.includes('--transport') ? args[args.indexOf('--transport') + 1] : 'stdio';
const port = args.includes('--port') ? parseInt(args[args.indexOf('--port') + 1]) : 8000;

const server = new UrlBuilderServer();

if (transport === 'http' || transport === 'sse') {
  server.runHttp(port).catch(console.error);
} else {
  server.runStdio().catch(console.error);
}
