#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
import { systemTools } from './tools/system.js';
import { networkTools } from './tools/network.js';
import { geoTools } from './tools/geo.js';
import { generatorTools } from './tools/generator.js';
import { dateTimeTools } from './tools/datetime.js';
import { securityTools } from './tools/security.js';
import { McpToolError, handleToolError } from './utils/errors.js';
import { RateLimiter, systemRateLimiter, networkRateLimiter, geoRateLimiter } from './utils/rate-limiter.js';
import { withProgress, ProgressReporter } from './utils/progress.js';

// Combine all tools
import { ToolKit, Tool } from './types.js';

// Update Tool interface to support progress reporting
declare module './types.js' {
  interface Tool {
    handler: (args: any, progress?: ProgressReporter) => Promise<{
      content: Array<{ type: string; text: string }>;
      isError?: boolean;
    }>;
  }
}

const allTools: ToolKit = {
  ...systemTools,
  ...networkTools,
  ...geoTools,
  ...generatorTools,
  ...dateTimeTools,
  ...securityTools
};

class ToolkitServer {
  private server: Server;
  private isShuttingDown: boolean = false;

  constructor() {
    this.server = new Server(
      {
        name: 'toolkit-mcp-server',
        version: '1.0.0',
      },
      {
        capabilities: {
          tools: {},
          progress: {}
        }
      }
    );

    this.setupHandlers();
    this.setupErrorHandling();
    this.setupCleanup();
  }

  private setupErrorHandling(): void {
    this.server.onerror = (error) => {
      console.error('[MCP Error]', error);
      if (!this.isShuttingDown) {
        // Attempt to notify clients of the error
        this.server.notification({
          method: 'notifications/server/error',
          params: {
            error: error instanceof Error ? error.message : 'Unknown error occurred'
          }
        }).catch(console.error);
      }
    };
  }

  private setupCleanup(): void {
    const cleanup = async () => {
      this.isShuttingDown = true;
      console.log('Shutting down toolkit-mcp-server...');
      
      try {
        // Notify clients of shutdown
        await this.server.notification({
          method: 'notifications/server/shutdown',
          params: {
            reason: 'Server is shutting down'
          }
        });

        // Close server connection
        await this.server.close();
        
        console.log('Server shutdown complete');
        process.exit(0);
      } catch (error) {
        console.error('Error during shutdown:', error);
        process.exit(1);
      }
    };

    // Handle termination signals
    process.on('SIGINT', cleanup);
    process.on('SIGTERM', cleanup);
    process.on('uncaughtException', (error) => {
      console.error('Uncaught exception:', error);
      cleanup().catch(console.error);
    });
  }

  private setupHandlers(): void {
    // Handle tool listing
    this.server.setRequestHandler(ListToolsRequestSchema, async () => {
      try {
        return {
          tools: Object.entries(allTools).map(([name, tool]) => ({
            name,
            description: tool.description,
            inputSchema: tool.inputSchema
          }))
        };
      } catch (error) {
        throw new McpToolError(
          ErrorCode.InternalError,
          'Failed to list tools',
          error instanceof Error ? error.message : undefined
        );
      }
    });

    // Handle tool execution
    this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
      try {
        const tool = allTools[request.params.name as keyof typeof allTools];
        
        if (!tool) {
          throw new McpToolError(
            ErrorCode.MethodNotFound,
            `Tool not found: ${request.params.name}`
          );
        }

        // Get appropriate rate limiter based on tool category
        let rateLimiter: RateLimiter;
        if (request.params.name.startsWith('get_system')) {
          rateLimiter = systemRateLimiter;
        } else if (request.params.name.startsWith('get_network') ||
                  request.params.name === 'ping_host' ||
                  request.params.name === 'traceroute') {
          rateLimiter = networkRateLimiter;
        } else if (request.params.name === 'geolocate') {
          rateLimiter = geoRateLimiter;
        } else {
          rateLimiter = systemRateLimiter; // default to system rate limiter
        }

        // Execute tool with rate limiting and progress reporting
        const result = await rateLimiter.withRateLimit(request.params.name, async () => {
          return await withProgress(
            this.server,
            {
              title: `Executing ${request.params.name}`,
              unit: 'operations'
            },
            async (progress) => {
              const toolResult = await tool.handler(request.params.arguments, progress);
              await progress.complete();
              return toolResult;
            }
          );
        });

        // Convert ToolResponse to ServerResult format
        return {
          content: result.content,
          _meta: result.isError ? { isError: true } : undefined
        };
      } catch (error) {
        if (error instanceof McpToolError) {
          throw error;
        }
        const errorResult = handleToolError(error);
        return {
          content: errorResult.content,
          _meta: { isError: true }
        };
      }
    });
  }

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

    // Handle process termination
    process.on('SIGINT', async () => {
      await this.server.close();
      process.exit(0);
    });

    process.on('SIGTERM', async () => {
      await this.server.close();
      process.exit(0);
    });
  }
}

// Start the server
const server = new ToolkitServer();
server.run().catch((error) => {
  console.error('Failed to start server:', error);
  process.exit(1);
});