/**
 * Salesforce MCP Server
 */

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
  ListResourcesRequestSchema,
  ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { SalesforceClient } from './clients/salesforce.js';
import { z } from 'zod';

export interface SalesforceServerConfig {
  accessToken: string;
  instanceUrl: string;
  apiVersion?: string;
}

export class SalesforceServer {
  private server: Server;
  private client: SalesforceClient;

  constructor(config: SalesforceServerConfig) {
    this.server = new Server(
      {
        name: 'salesforce-mcp-server',
        version: '1.0.0',
      },
      {
        capabilities: {
          tools: {},
          resources: {},
        },
      }
    );

    this.client = new SalesforceClient({
      accessToken: config.accessToken,
      instanceUrl: config.instanceUrl,
      apiVersion: config.apiVersion,
    });

    this.setupHandlers();
  }

  /**
   * Setup MCP handlers
   */
  private setupHandlers(): void {
    // List available tools
    this.server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          // Foundation tools - additional tools can be added via tool modules later
          {
            name: 'salesforce_query',
            description: 'Execute a SOQL query against Salesforce',
            inputSchema: {
              type: 'object',
              properties: {
                query: {
                  type: 'string',
                  description: 'SOQL query string',
                },
              },
              required: ['query'],
            },
          },
          {
            name: 'salesforce_create_record',
            description: 'Create a new Salesforce record',
            inputSchema: {
              type: 'object',
              properties: {
                objectType: {
                  type: 'string',
                  description: 'Salesforce object type (e.g., Account, Contact)',
                },
                data: {
                  type: 'object',
                  description: 'Record data',
                },
              },
              required: ['objectType', 'data'],
            },
          },
          {
            name: 'salesforce_update_record',
            description: 'Update an existing Salesforce record',
            inputSchema: {
              type: 'object',
              properties: {
                objectType: {
                  type: 'string',
                  description: 'Salesforce object type',
                },
                id: {
                  type: 'string',
                  description: 'Record ID',
                },
                data: {
                  type: 'object',
                  description: 'Fields to update',
                },
              },
              required: ['objectType', 'id', 'data'],
            },
          },
          {
            name: 'salesforce_delete_record',
            description: 'Delete a Salesforce record',
            inputSchema: {
              type: 'object',
              properties: {
                objectType: {
                  type: 'string',
                  description: 'Salesforce object type',
                },
                id: {
                  type: 'string',
                  description: 'Record ID',
                },
              },
              required: ['objectType', 'id'],
            },
          },
          {
            name: 'salesforce_describe_object',
            description: 'Get metadata for a Salesforce object',
            inputSchema: {
              type: 'object',
              properties: {
                objectType: {
                  type: 'string',
                  description: 'Salesforce object type',
                },
              },
              required: ['objectType'],
            },
          },
        ],
      };
    });

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

      try {
        // Route to appropriate handler
        switch (name) {
          case 'salesforce_query':
            return await this.handleQuery(args as { query: string });

          case 'salesforce_create_record':
            return await this.handleCreateRecord(
              args as { objectType: string; data: Record<string, unknown> }
            );

          case 'salesforce_update_record':
            return await this.handleUpdateRecord(
              args as { objectType: string; id: string; data: Record<string, unknown> }
            );

          case 'salesforce_delete_record':
            return await this.handleDeleteRecord(
              args as { objectType: string; id: string }
            );

          case 'salesforce_describe_object':
            return await this.handleDescribeObject(
              args as { objectType: string }
            );

          default:
            throw new Error(`Unknown tool: ${name}`);
        }
      } catch (error) {
        return this.formatError(error);
      }
    });

    // List resources (for UI apps)
    this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
      return {
        resources: [
          {
            uri: 'salesforce://dashboard',
            name: 'Salesforce Dashboard',
            description: 'Overview of Salesforce data and metrics',
            mimeType: 'application/json',
          },
        ],
      };
    });

    // Read resource
    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
      const { uri } = request.params;

      if (uri === 'salesforce://dashboard') {
        const limits = this.client.getApiLimits();
        return {
          contents: [
            {
              uri,
              mimeType: 'application/json',
              text: JSON.stringify(
                {
                  apiLimits: limits,
                  instanceUrl: this.client['instanceUrl'],
                },
                null,
                2
              ),
            },
          ],
        };
      }

      throw new Error(`Unknown resource: ${uri}`);
    });
  }

  /**
   * Handle SOQL query
   */
  private async handleQuery(args: { query: string }) {
    const result = await this.client.query(args.query);
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(result, null, 2),
        },
      ],
    };
  }

  /**
   * Handle create record
   */
  private async handleCreateRecord(args: {
    objectType: string;
    data: Record<string, unknown>;
  }) {
    const result = await this.client.createRecord(args.objectType, args.data);
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(result, null, 2),
        },
      ],
    };
  }

  /**
   * Handle update record
   */
  private async handleUpdateRecord(args: {
    objectType: string;
    id: string;
    data: Record<string, unknown>;
  }) {
    await this.client.updateRecord(args.objectType, args.id, args.data);
    return {
      content: [
        {
          type: 'text',
          text: 'Record updated successfully',
        },
      ],
    };
  }

  /**
   * Handle delete record
   */
  private async handleDeleteRecord(args: { objectType: string; id: string }) {
    await this.client.deleteRecord(args.objectType, args.id);
    return {
      content: [
        {
          type: 'text',
          text: 'Record deleted successfully',
        },
      ],
    };
  }

  /**
   * Handle describe object
   */
  private async handleDescribeObject(args: { objectType: string }) {
    const result = await this.client.describe(args.objectType);
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(result, null, 2),
        },
      ],
    };
  }

  /**
   * Format error response
   */
  private formatError(error: unknown) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(
            {
              error: true,
              message,
            },
            null,
            2
          ),
        },
      ],
      isError: true,
    };
  }

  /**
   * Start the server
   */
  public async start(): Promise<void> {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Salesforce MCP Server running on stdio');
  }

  /**
   * Graceful shutdown
   */
  public async close(): Promise<void> {
    await this.server.close();
  }

  /**
   * Get the underlying Server instance
   */
  public getServer(): Server {
    return this.server;
  }

  /**
   * Get the Salesforce client
   */
  public getClient(): SalesforceClient {
    return this.client;
  }
}
