import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
  Tool,
} from '@modelcontextprotocol/sdk/types.js';
import { AirtableClient } from './clients/airtable.js';
import { z, ZodSchema } from 'zod';
import { getTools as getBasesTools } from './tools/bases.js';
import { getTools as getTablesTools } from './tools/tables.js';
import { getTools as getRecordsTools } from './tools/records.js';
import { getTools as getFieldsTools } from './tools/fields.js';
import { getTools as getViewsTools } from './tools/views.js';
import { getTools as getWebhooksTools } from './tools/webhooks.js';
import { getTools as getAutomationsTools } from './tools/automations.js';

export interface AirtableServerConfig {
  apiKey: string;
  serverName?: string;
  serverVersion?: string;
}

interface MCPTool {
  name: string;
  description: string;
  inputSchema: ZodSchema;
  execute: (args: any) => Promise<unknown>;
}

export class AirtableServer {
  private server: Server;
  private client: AirtableClient;
  private toolRegistry: Map<string, MCPTool>;

  constructor(config: AirtableServerConfig) {
    this.client = new AirtableClient({
      apiKey: config.apiKey,
    });

    this.server = new Server(
      {
        name: config.serverName || '@mcpengine/airtable',
        version: config.serverVersion || '1.0.0',
      },
      {
        capabilities: {
          tools: {},
        },
      }
    );

    this.toolRegistry = new Map();
    this.registerTools();
    this.setupHandlers();
  }

  private registerTools(): void {
    const allTools = [
      ...getBasesTools(this.client),
      ...getTablesTools(this.client),
      ...getRecordsTools(this.client),
      ...getFieldsTools(this.client),
      ...getViewsTools(this.client),
      ...getWebhooksTools(this.client),
      ...getAutomationsTools(this.client),
    ];

    for (const tool of allTools) {
      this.toolRegistry.set(tool.name, tool);
    }
  }

  private setupHandlers(): void {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: this.getTools(),
    }));

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

      try {
        const result = await this.handleToolCall(name, args || {});
        return {
          content: [
            {
              type: 'text' as const,
              text: JSON.stringify(result, null, 2),
            },
          ],
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Unknown error';
        return {
          content: [
            {
              type: 'text' as const,
              text: JSON.stringify({ error: errorMessage }, null, 2),
            },
          ],
          isError: true,
        };
      }
    });
  }

  private zodToJsonSchema(schema: ZodSchema): {
    type: 'object';
    properties?: { [key: string]: object };
    required?: string[];
  } {
    // Convert Zod schema to JSON Schema format for MCP
    // This is a simplified conversion - in production, use zod-to-json-schema library
    return {
      type: 'object' as const,
      properties: {},
      additionalProperties: true,
    } as any;
  }

  private getTools(): Tool[] {
    const tools: Tool[] = [];
    
    for (const [name, tool] of this.toolRegistry.entries()) {
      tools.push({
        name,
        description: tool.description,
        inputSchema: this.zodToJsonSchema(tool.inputSchema),
      });
    }
    
    return tools;
  }

  private async handleToolCall(name: string, args: Record<string, unknown>): Promise<unknown> {
    const tool = this.toolRegistry.get(name);
    
    if (!tool) {
      throw new Error(`Unknown tool: ${name}`);
    }

    // Validate and parse args with Zod
    const parsedArgs = tool.inputSchema.parse(args);
    
    // Execute the tool
    return await tool.execute(parsedArgs);
  }

  async connect(transport: StdioServerTransport): Promise<void> {
    await this.server.connect(transport);
  }

  getServer(): Server {
    return this.server;
  }
}
