/**
 * HubSpot MCP Server
 * Main server implementation with lazy-loaded tool modules
 */

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 { HubSpotClient } from './clients/hubspot.js';

export interface HubSpotServerConfig {
  accessToken: string;
  serverName?: string;
  serverVersion?: string;
}

export class HubSpotServer {
  private server: Server;
  private client: HubSpotClient;
  private toolModules: Map<string, any> = new Map();

  constructor(config: HubSpotServerConfig) {
    this.client = new HubSpotClient({
      accessToken: config.accessToken,
    });

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

    this.setupHandlers();
  }

  /**
   * Setup MCP protocol handlers
   */
  private setupHandlers(): void {
    // List available tools
    this.server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          // CRM Objects
          {
            name: 'hubspot_contacts_list',
            description: 'List contacts with optional filtering and pagination',
            inputSchema: {
              type: 'object',
              properties: {
                limit: { type: 'number', description: 'Max results (1-100)' },
                after: { type: 'string', description: 'Pagination cursor' },
                properties: {
                  type: 'array',
                  items: { type: 'string' },
                  description: 'Properties to return',
                },
              },
            },
          },
          {
            name: 'hubspot_contacts_get',
            description: 'Get a contact by ID',
            inputSchema: {
              type: 'object',
              properties: {
                contactId: { type: 'string', description: 'Contact ID' },
                properties: {
                  type: 'array',
                  items: { type: 'string' },
                  description: 'Properties to return',
                },
              },
              required: ['contactId'],
            },
          },
          {
            name: 'hubspot_contacts_create',
            description: 'Create a new contact',
            inputSchema: {
              type: 'object',
              properties: {
                properties: {
                  type: 'object',
                  description: 'Contact properties (email, firstname, lastname, etc.)',
                },
              },
              required: ['properties'],
            },
          },
          {
            name: 'hubspot_contacts_update',
            description: 'Update a contact',
            inputSchema: {
              type: 'object',
              properties: {
                contactId: { type: 'string', description: 'Contact ID' },
                properties: {
                  type: 'object',
                  description: 'Properties to update',
                },
              },
              required: ['contactId', 'properties'],
            },
          },
          {
            name: 'hubspot_contacts_search',
            description: 'Search contacts with filters',
            inputSchema: {
              type: 'object',
              properties: {
                filterGroups: {
                  type: 'array',
                  description: 'Filter groups for search',
                },
                properties: {
                  type: 'array',
                  items: { type: 'string' },
                  description: 'Properties to return',
                },
                limit: { type: 'number', description: 'Max results' },
              },
            },
          },
          // Companies
          {
            name: 'hubspot_companies_list',
            description: 'List companies',
            inputSchema: { type: 'object', properties: {} },
          },
          {
            name: 'hubspot_companies_get',
            description: 'Get a company by ID',
            inputSchema: {
              type: 'object',
              properties: {
                companyId: { type: 'string', description: 'Company ID' },
              },
              required: ['companyId'],
            },
          },
          {
            name: 'hubspot_companies_create',
            description: 'Create a new company',
            inputSchema: {
              type: 'object',
              properties: {
                properties: { type: 'object', description: 'Company properties' },
              },
              required: ['properties'],
            },
          },
          // Deals
          {
            name: 'hubspot_deals_list',
            description: 'List deals',
            inputSchema: { type: 'object', properties: {} },
          },
          {
            name: 'hubspot_deals_get',
            description: 'Get a deal by ID',
            inputSchema: {
              type: 'object',
              properties: {
                dealId: { type: 'string', description: 'Deal ID' },
              },
              required: ['dealId'],
            },
          },
          {
            name: 'hubspot_deals_create',
            description: 'Create a new deal',
            inputSchema: {
              type: 'object',
              properties: {
                properties: { type: 'object', description: 'Deal properties' },
              },
              required: ['properties'],
            },
          },
          // Tickets
          {
            name: 'hubspot_tickets_list',
            description: 'List tickets',
            inputSchema: { type: 'object', properties: {} },
          },
          {
            name: 'hubspot_tickets_create',
            description: 'Create a new ticket',
            inputSchema: {
              type: 'object',
              properties: {
                properties: { type: 'object', description: 'Ticket properties' },
              },
              required: ['properties'],
            },
          },
          // Associations
          {
            name: 'hubspot_associations_get',
            description: 'Get associations for an object',
            inputSchema: {
              type: 'object',
              properties: {
                fromObjectType: { type: 'string' },
                fromObjectId: { type: 'string' },
                toObjectType: { type: 'string' },
              },
              required: ['fromObjectType', 'fromObjectId', 'toObjectType'],
            },
          },
          {
            name: 'hubspot_associations_create',
            description: 'Create an association between objects',
            inputSchema: {
              type: 'object',
              properties: {
                fromObjectType: { type: 'string' },
                fromObjectId: { type: 'string' },
                toObjectType: { type: 'string' },
                toObjectId: { type: 'string' },
                associationTypeId: { type: 'number' },
              },
              required: [
                'fromObjectType',
                'fromObjectId',
                'toObjectType',
                'toObjectId',
                'associationTypeId',
              ],
            },
          },
          // Pipelines
          {
            name: 'hubspot_pipelines_list',
            description: 'List pipelines for an object type',
            inputSchema: {
              type: 'object',
              properties: {
                objectType: {
                  type: 'string',
                  description: 'Object type (deals, tickets, etc.)',
                },
              },
              required: ['objectType'],
            },
          },
          // Owners
          {
            name: 'hubspot_owners_list',
            description: 'List all owners',
            inputSchema: { type: 'object', properties: {} },
          },
          // Properties
          {
            name: 'hubspot_properties_list',
            description: 'List properties for an object type',
            inputSchema: {
              type: 'object',
              properties: {
                objectType: { type: 'string', description: 'Object type' },
              },
              required: ['objectType'],
            },
          },
        ],
      };
    });

    // Handle tool calls
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      try {
        return await this.handleToolCall(request.params.name, request.params.arguments ?? {});
      } catch (error: any) {
        return {
          content: [
            {
              type: 'text',
              text: `Error: ${error.message}`,
            },
          ],
          isError: true,
        };
      }
    });

    // List resources (for UI apps)
    this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
      return {
        resources: [
          {
            uri: 'hubspot://contacts',
            name: 'HubSpot Contacts',
            description: 'Access to HubSpot contacts data',
            mimeType: 'application/json',
          },
          {
            uri: 'hubspot://companies',
            name: 'HubSpot Companies',
            description: 'Access to HubSpot companies data',
            mimeType: 'application/json',
          },
          {
            uri: 'hubspot://deals',
            name: 'HubSpot Deals',
            description: 'Access to HubSpot deals data',
            mimeType: 'application/json',
          },
        ],
      };
    });

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

      // Parse URI and return appropriate data
      // Implementation will be in tool modules
      return {
        contents: [
          {
            uri,
            mimeType: 'application/json',
            text: JSON.stringify({ message: 'Resource handler not yet implemented' }),
          },
        ],
      };
    });
  }

  /**
   * Handle tool calls with lazy loading
   */
  private async handleToolCall(name: string, args: any): Promise<any> {
    // Tool routing - this is where lazy-loaded modules will be imported
    // For now, we'll implement basic routing structure

    const [prefix, module, action] = name.split('_');

    if (prefix !== 'hubspot') {
      throw new Error(`Unknown tool prefix: ${prefix}`);
    }

    // Route to appropriate handler based on module
    switch (module) {
      case 'contacts':
        return this.handleContactsTool(action, args);
      case 'companies':
        return this.handleCompaniesTool(action, args);
      case 'deals':
        return this.handleDealsTool(action, args);
      case 'tickets':
        return this.handleTicketsTool(action, args);
      case 'associations':
        return this.handleAssociationsTool(action, args);
      case 'pipelines':
        return this.handlePipelinesTool(action, args);
      case 'owners':
        return this.handleOwnersTool(action, args);
      case 'properties':
        return this.handlePropertiesTool(action, args);
      default:
        throw new Error(`Unknown module: ${module}`);
    }
  }

  /**
   * Placeholder tool handlers - will be replaced with lazy-loaded modules
   */
  private async handleContactsTool(action: string, args: any): Promise<any> {
    switch (action) {
      case 'list':
        const contacts = await this.client.listObjects('contacts', args);
        return {
          content: [{ type: 'text', text: JSON.stringify(contacts, null, 2) }],
        };
      case 'get':
        const contact = await this.client.getObject('contacts', args.contactId, args.properties);
        return {
          content: [{ type: 'text', text: JSON.stringify(contact, null, 2) }],
        };
      case 'create':
        const newContact = await this.client.createObject('contacts', args.properties);
        return {
          content: [{ type: 'text', text: JSON.stringify(newContact, null, 2) }],
        };
      case 'update':
        const updated = await this.client.updateObject('contacts', args.contactId, args.properties);
        return {
          content: [{ type: 'text', text: JSON.stringify(updated, null, 2) }],
        };
      case 'search':
        const results = await this.client.searchObjects('contacts', args);
        return {
          content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
        };
      default:
        throw new Error(`Unknown contacts action: ${action}`);
    }
  }

  private async handleCompaniesTool(action: string, args: any): Promise<any> {
    switch (action) {
      case 'list':
        const companies = await this.client.listObjects('companies', args);
        return {
          content: [{ type: 'text', text: JSON.stringify(companies, null, 2) }],
        };
      case 'get':
        const company = await this.client.getObject('companies', args.companyId);
        return {
          content: [{ type: 'text', text: JSON.stringify(company, null, 2) }],
        };
      case 'create':
        const newCompany = await this.client.createObject('companies', args.properties);
        return {
          content: [{ type: 'text', text: JSON.stringify(newCompany, null, 2) }],
        };
      default:
        throw new Error(`Unknown companies action: ${action}`);
    }
  }

  private async handleDealsTool(action: string, args: any): Promise<any> {
    switch (action) {
      case 'list':
        const deals = await this.client.listObjects('deals', args);
        return {
          content: [{ type: 'text', text: JSON.stringify(deals, null, 2) }],
        };
      case 'get':
        const deal = await this.client.getObject('deals', args.dealId);
        return {
          content: [{ type: 'text', text: JSON.stringify(deal, null, 2) }],
        };
      case 'create':
        const newDeal = await this.client.createObject('deals', args.properties);
        return {
          content: [{ type: 'text', text: JSON.stringify(newDeal, null, 2) }],
        };
      default:
        throw new Error(`Unknown deals action: ${action}`);
    }
  }

  private async handleTicketsTool(action: string, args: any): Promise<any> {
    switch (action) {
      case 'list':
        const tickets = await this.client.listObjects('tickets', args);
        return {
          content: [{ type: 'text', text: JSON.stringify(tickets, null, 2) }],
        };
      case 'create':
        const newTicket = await this.client.createObject('tickets', args.properties);
        return {
          content: [{ type: 'text', text: JSON.stringify(newTicket, null, 2) }],
        };
      default:
        throw new Error(`Unknown tickets action: ${action}`);
    }
  }

  private async handleAssociationsTool(action: string, args: any): Promise<any> {
    switch (action) {
      case 'get':
        const associations = await this.client.getAssociations(
          args.fromObjectType,
          args.fromObjectId,
          args.toObjectType
        );
        return {
          content: [{ type: 'text', text: JSON.stringify(associations, null, 2) }],
        };
      case 'create':
        await this.client.createAssociation(
          args.fromObjectType,
          args.fromObjectId,
          args.toObjectType,
          args.toObjectId,
          args.associationTypeId
        );
        return {
          content: [{ type: 'text', text: 'Association created successfully' }],
        };
      default:
        throw new Error(`Unknown associations action: ${action}`);
    }
  }

  private async handlePipelinesTool(action: string, args: any): Promise<any> {
    switch (action) {
      case 'list':
        const pipelines = await this.client.getPipelines(args.objectType);
        return {
          content: [{ type: 'text', text: JSON.stringify(pipelines, null, 2) }],
        };
      default:
        throw new Error(`Unknown pipelines action: ${action}`);
    }
  }

  private async handleOwnersTool(action: string, args: any): Promise<any> {
    switch (action) {
      case 'list':
        const owners = await this.client.getOwners();
        return {
          content: [{ type: 'text', text: JSON.stringify(owners, null, 2) }],
        };
      default:
        throw new Error(`Unknown owners action: ${action}`);
    }
  }

  private async handlePropertiesTool(action: string, args: any): Promise<any> {
    switch (action) {
      case 'list':
        const properties = await this.client.getProperties(args.objectType);
        return {
          content: [{ type: 'text', text: JSON.stringify(properties, null, 2) }],
        };
      default:
        throw new Error(`Unknown properties action: ${action}`);
    }
  }

  /**
   * Start the server
   */
  async start(): Promise<void> {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
  }

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

  /**
   * Get the HubSpot client
   */
  getClient(): HubSpotClient {
    return this.client;
  }
}
