import { GHLApiClient } from '../clients/ghl-api-client.js';
import {
  MCPGetAllObjectsParams,
  MCPCreateObjectSchemaParams,
  MCPGetObjectSchemaParams,
  MCPUpdateObjectSchemaParams,
  MCPCreateObjectRecordParams,
  MCPGetObjectRecordParams,
  MCPUpdateObjectRecordParams,
  MCPDeleteObjectRecordParams,
  MCPSearchObjectRecordsParams,
  GHLGetObjectSchemaRequest,
  GHLCreateObjectSchemaRequest,
  GHLUpdateObjectSchemaRequest,
  GHLCreateObjectRecordRequest,
  GHLUpdateObjectRecordRequest,
  GHLSearchObjectRecordsRequest
} from '../types/ghl-types.js';

export interface Tool {
  name: string;
  description: string;
  inputSchema: {
    type: string;
    properties: Record<string, any>;
    required: string[];
  };
}

/**
 * ObjectTools class for GoHighLevel Custom Objects API endpoints
 * Handles both object schema management and record operations for custom and standard objects
 */
export class ObjectTools {
  constructor(private ghlClient: GHLApiClient) {}

  /**
   * Get all available Custom Objects tool definitions
   */
  getToolDefinitions(): Tool[] {
    return [
      {
        name: 'get_all_objects',
        description: 'Get all objects (custom and standard) for a location including contact, opportunity, business, and custom objects',
        inputSchema: {
          type: 'object',
          properties: {
            locationId: { 
              type: 'string', 
              description: 'Location ID (uses default if not provided)'
            }
          },
          required: []
        }
      },
      {
        name: 'create_object_schema',
        description: 'Create a new custom object schema with labels, key, and primary display property',
        inputSchema: {
          type: 'object',
          properties: {
            labels: {
              type: 'object',
              description: 'Singular and plural names for the custom object',
              properties: {
                singular: { type: 'string', description: 'Singular name (e.g., "Pet")' },
                plural: { type: 'string', description: 'Plural name (e.g., "Pets")' }
              },
              required: ['singular', 'plural']
            },
            key: { 
              type: 'string', 
              description: 'Unique key for the object (e.g., "custom_objects.pet"). The "custom_objects." prefix is added automatically if not included'
            },
            description: { 
              type: 'string', 
              description: 'Description of the custom object'
            },
            locationId: { 
              type: 'string', 
              description: 'Location ID (uses default if not provided)'
            },
            primaryDisplayPropertyDetails: {
              type: 'object',
              description: 'Primary property configuration for display',
              properties: {
                key: { type: 'string', description: 'Property key (e.g., "custom_objects.pet.name")' },
                name: { type: 'string', description: 'Display name (e.g., "Pet Name")' },
                dataType: { type: 'string', description: 'Data type (TEXT or NUMERICAL)', enum: ['TEXT', 'NUMERICAL'] }
              },
              required: ['key', 'name', 'dataType']
            }
          },
          required: ['labels', 'key', 'primaryDisplayPropertyDetails']
        }
      },
      {
        name: 'get_object_schema',
        description: 'Get object schema details by key including all fields and properties for custom or standard objects',
        inputSchema: {
          type: 'object',
          properties: {
            key: { 
              type: 'string', 
              description: 'Object key (e.g., "custom_objects.pet" for custom objects, "contact" for standard objects)'
            },
            locationId: { 
              type: 'string', 
              description: 'Location ID (uses default if not provided)'
            },
            fetchProperties: { 
              type: 'boolean', 
              description: 'Whether to fetch all standard/custom fields of the object',
              default: true
            }
          },
          required: ['key']
        }
      },
      {
        name: 'update_object_schema',
        description: 'Update object schema properties including labels, description, and searchable fields',
        inputSchema: {
          type: 'object',
          properties: {
            key: { 
              type: 'string', 
              description: 'Object key to update'
            },
            labels: {
              type: 'object',
              description: 'Updated singular and plural names (optional)',
              properties: {
                singular: { type: 'string', description: 'Updated singular name' },
                plural: { type: 'string', description: 'Updated plural name' }
              }
            },
            description: { 
              type: 'string', 
              description: 'Updated description'
            },
            locationId: { 
              type: 'string', 
              description: 'Location ID (uses default if not provided)'
            },
            searchableProperties: {
              type: 'array',
              description: 'Array of field keys that should be searchable (e.g., ["custom_objects.pet.name", "custom_objects.pet.breed"])',
              items: { type: 'string' }
            }
          },
          required: ['key', 'searchableProperties']
        }
      },
      {
        name: 'create_object_record',
        description: 'Create a new record in a custom or standard object with properties, owner, and followers',
        inputSchema: {
          type: 'object',
          properties: {
            schemaKey: { 
              type: 'string', 
              description: 'Schema key of the object (e.g., "custom_objects.pet", "business")'
            },
            properties: {
              type: 'object',
              description: 'Record properties as key-value pairs (e.g., {"name": "Buddy", "breed": "Golden Retriever"})'
            },
            locationId: { 
              type: 'string', 
              description: 'Location ID (uses default if not provided)'
            },
            owner: {
              type: 'array',
              description: 'Array of user IDs who own this record (limited to 1, only for custom objects)',
              items: { type: 'string' },
              maxItems: 1
            },
            followers: {
              type: 'array',
              description: 'Array of user IDs who follow this record (limited to 10)',
              items: { type: 'string' },
              maxItems: 10
            }
          },
          required: ['schemaKey', 'properties']
        }
      },
      {
        name: 'get_object_record',
        description: 'Get a specific record by ID from a custom or standard object',
        inputSchema: {
          type: 'object',
          properties: {
            schemaKey: { 
              type: 'string', 
              description: 'Schema key of the object'
            },
            recordId: { 
              type: 'string', 
              description: 'ID of the record to retrieve'
            }
          },
          required: ['schemaKey', 'recordId']
        }
      },
      {
        name: 'update_object_record',
        description: 'Update an existing record in a custom or standard object',
        inputSchema: {
          type: 'object',
          properties: {
            schemaKey: { 
              type: 'string', 
              description: 'Schema key of the object'
            },
            recordId: { 
              type: 'string', 
              description: 'ID of the record to update'
            },
            properties: {
              type: 'object',
              description: 'Updated record properties as key-value pairs'
            },
            locationId: { 
              type: 'string', 
              description: 'Location ID (uses default if not provided)'
            },
            owner: {
              type: 'array',
              description: 'Updated array of user IDs who own this record',
              items: { type: 'string' },
              maxItems: 1
            },
            followers: {
              type: 'array',
              description: 'Updated array of user IDs who follow this record',
              items: { type: 'string' },
              maxItems: 10
            }
          },
          required: ['schemaKey', 'recordId']
        }
      },
      {
        name: 'delete_object_record',
        description: 'Delete a record from a custom or standard object',
        inputSchema: {
          type: 'object',
          properties: {
            schemaKey: { 
              type: 'string', 
              description: 'Schema key of the object'
            },
            recordId: { 
              type: 'string', 
              description: 'ID of the record to delete'
            }
          },
          required: ['schemaKey', 'recordId']
        }
      },
      {
        name: 'search_object_records',
        description: 'Search records within a custom or standard object using searchable properties',
        inputSchema: {
          type: 'object',
          properties: {
            schemaKey: { 
              type: 'string', 
              description: 'Schema key of the object to search in'
            },
            query: { 
              type: 'string', 
              description: 'Search query using searchable properties (e.g., "name:Buddy" to search for records with name Buddy)'
            },
            locationId: { 
              type: 'string', 
              description: 'Location ID (uses default if not provided)'
            },
            page: { 
              type: 'number', 
              description: 'Page number for pagination',
              default: 1,
              minimum: 1
            },
            pageLimit: { 
              type: 'number', 
              description: 'Number of records per page',
              default: 10,
              minimum: 1,
              maximum: 100
            },
            searchAfter: {
              type: 'array',
              description: 'Cursor for pagination (returned from previous search)',
              items: { type: 'string' }
            }
          },
          required: ['schemaKey', 'query']
        }
      }
    ];
  }

  /**
   * Execute an object tool by name with given arguments
   */
  async executeTool(name: string, args: any): Promise<any> {
    switch (name) {
      case 'get_all_objects':
        return this.getAllObjects(args as MCPGetAllObjectsParams);
      
      case 'create_object_schema':
        return this.createObjectSchema(args as MCPCreateObjectSchemaParams);
      
      case 'get_object_schema':
        return this.getObjectSchema(args as MCPGetObjectSchemaParams);
      
      case 'update_object_schema':
        return this.updateObjectSchema(args as MCPUpdateObjectSchemaParams);
      
      case 'create_object_record':
        return this.createObjectRecord(args as MCPCreateObjectRecordParams);
      
      case 'get_object_record':
        return this.getObjectRecord(args as MCPGetObjectRecordParams);
      
      case 'update_object_record':
        return this.updateObjectRecord(args as MCPUpdateObjectRecordParams);
      
      case 'delete_object_record':
        return this.deleteObjectRecord(args as MCPDeleteObjectRecordParams);
      
      case 'search_object_records':
        return this.searchObjectRecords(args as MCPSearchObjectRecordsParams);
      
      default:
        throw new Error(`Unknown object tool: ${name}`);
    }
  }

  /**
   * GET ALL OBJECTS
   */
  private async getAllObjects(params: MCPGetAllObjectsParams = {}): Promise<{ success: boolean; objects: any[]; message: string }> {
    try {
      const response = await this.ghlClient.getObjectsByLocation(params.locationId);
      
      if (!response.success || !response.data) {
        const errorMsg = response.error?.message || 'Unknown API error';
        throw new Error(`API request failed: ${errorMsg}`);
      }

      const objects = Array.isArray(response.data.objects) ? response.data.objects : [];
      
      return {
        success: true,
        objects,
        message: `Retrieved ${objects.length} objects for location`
      };
    } catch (error) {
      throw new Error(`Failed to get objects: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * CREATE OBJECT SCHEMA
   */
  private async createObjectSchema(params: MCPCreateObjectSchemaParams): Promise<{ success: boolean; object: any; message: string }> {
    try {
      const schemaData: GHLCreateObjectSchemaRequest = {
        labels: params.labels,
        key: params.key,
        description: params.description,
        locationId: params.locationId || this.ghlClient.getConfig().locationId,
        primaryDisplayPropertyDetails: params.primaryDisplayPropertyDetails
      };

      const response = await this.ghlClient.createObjectSchema(schemaData);
      
      if (!response.success || !response.data) {
        const errorMsg = response.error?.message || 'Unknown API error';
        throw new Error(`API request failed: ${errorMsg}`);
      }
      
      return {
        success: true,
        object: response.data.object,
        message: `Custom object schema created successfully with key: ${response.data.object.key}`
      };
    } catch (error) {
      throw new Error(`Failed to create object schema: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * GET OBJECT SCHEMA
   */
  private async getObjectSchema(params: MCPGetObjectSchemaParams): Promise<{ success: boolean; object: any; fields?: any[]; cache?: boolean; message: string }> {
    try {
      const requestParams: GHLGetObjectSchemaRequest = {
        key: params.key,
        locationId: params.locationId || this.ghlClient.getConfig().locationId,
        fetchProperties: params.fetchProperties
      };

      const response = await this.ghlClient.getObjectSchema(requestParams);
      
      if (!response.success || !response.data) {
        const errorMsg = response.error?.message || 'Unknown API error';
        throw new Error(`API request failed: ${errorMsg}`);
      }
      
      return {
        success: true,
        object: response.data.object,
        fields: response.data.fields,
        cache: response.data.cache,
        message: `Object schema retrieved successfully for key: ${params.key}`
      };
    } catch (error) {
      throw new Error(`Failed to get object schema: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * UPDATE OBJECT SCHEMA
   */
  private async updateObjectSchema(params: MCPUpdateObjectSchemaParams): Promise<{ success: boolean; object: any; message: string }> {
    try {
      const updateData: GHLUpdateObjectSchemaRequest = {
        labels: params.labels,
        description: params.description,
        locationId: params.locationId || this.ghlClient.getConfig().locationId,
        searchableProperties: params.searchableProperties
      };

      const response = await this.ghlClient.updateObjectSchema(params.key, updateData);
      
      if (!response.success || !response.data) {
        const errorMsg = response.error?.message || 'Unknown API error';
        throw new Error(`API request failed: ${errorMsg}`);
      }
      
      return {
        success: true,
        object: response.data.object,
        message: `Object schema updated successfully for key: ${params.key}`
      };
    } catch (error) {
      throw new Error(`Failed to update object schema: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * CREATE OBJECT RECORD
   */
  private async createObjectRecord(params: MCPCreateObjectRecordParams): Promise<{ success: boolean; record: any; recordId: string; message: string }> {
    try {
      const recordData: GHLCreateObjectRecordRequest = {
        properties: params.properties,
        locationId: params.locationId || this.ghlClient.getConfig().locationId,
        owner: params.owner,
        followers: params.followers
      };

      const response = await this.ghlClient.createObjectRecord(params.schemaKey, recordData);
      
      if (!response.success || !response.data) {
        const errorMsg = response.error?.message || 'Unknown API error';
        throw new Error(`API request failed: ${errorMsg}`);
      }
      
      return {
        success: true,
        record: response.data.record,
        recordId: response.data.record.id,
        message: `Record created successfully in ${params.schemaKey} with ID: ${response.data.record.id}`
      };
    } catch (error) {
      throw new Error(`Failed to create object record: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * GET OBJECT RECORD
   */
  private async getObjectRecord(params: MCPGetObjectRecordParams): Promise<{ success: boolean; record: any; message: string }> {
    try {
      const response = await this.ghlClient.getObjectRecord(params.schemaKey, params.recordId);
      
      if (!response.success || !response.data) {
        const errorMsg = response.error?.message || 'Unknown API error';
        throw new Error(`API request failed: ${errorMsg}`);
      }
      
      return {
        success: true,
        record: response.data.record,
        message: `Record retrieved successfully from ${params.schemaKey}`
      };
    } catch (error) {
      throw new Error(`Failed to get object record: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * UPDATE OBJECT RECORD
   */
  private async updateObjectRecord(params: MCPUpdateObjectRecordParams): Promise<{ success: boolean; record: any; message: string }> {
    try {
      const updateData: GHLUpdateObjectRecordRequest = {
        properties: params.properties,
        locationId: params.locationId || this.ghlClient.getConfig().locationId,
        owner: params.owner,
        followers: params.followers
      };

      const response = await this.ghlClient.updateObjectRecord(params.schemaKey, params.recordId, updateData);
      
      if (!response.success || !response.data) {
        const errorMsg = response.error?.message || 'Unknown API error';
        throw new Error(`API request failed: ${errorMsg}`);
      }
      
      return {
        success: true,
        record: response.data.record,
        message: `Record updated successfully in ${params.schemaKey}`
      };
    } catch (error) {
      throw new Error(`Failed to update object record: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * DELETE OBJECT RECORD
   */
  private async deleteObjectRecord(params: MCPDeleteObjectRecordParams): Promise<{ success: boolean; deletedId: string; message: string }> {
    try {
      const response = await this.ghlClient.deleteObjectRecord(params.schemaKey, params.recordId);
      
      if (!response.success || !response.data) {
        const errorMsg = response.error?.message || 'Unknown API error';
        throw new Error(`API request failed: ${errorMsg}`);
      }
      
      return {
        success: true,
        deletedId: response.data.id,
        message: `Record deleted successfully from ${params.schemaKey}`
      };
    } catch (error) {
      throw new Error(`Failed to delete object record: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * SEARCH OBJECT RECORDS
   */
  private async searchObjectRecords(params: MCPSearchObjectRecordsParams): Promise<{ success: boolean; records: any[]; total: number; message: string }> {
    try {
      const searchData: GHLSearchObjectRecordsRequest = {
        locationId: params.locationId || this.ghlClient.getConfig().locationId,
        page: params.page || 1,
        pageLimit: params.pageLimit || 10,
        query: params.query,
        searchAfter: params.searchAfter || []
      };

      const response = await this.ghlClient.searchObjectRecords(params.schemaKey, searchData);
      
      if (!response.success || !response.data) {
        const errorMsg = response.error?.message || 'Unknown API error';
        throw new Error(`API request failed: ${errorMsg}`);
      }

      const records = Array.isArray(response.data.records) ? response.data.records : [];
      
      return {
        success: true,
        records,
        total: response.data.total,
        message: `Found ${records.length} records in ${params.schemaKey} (${response.data.total} total)`
      };
    } catch (error) {
      throw new Error(`Failed to search object records: ${error instanceof Error ? error.message : String(error)}`);
    }
  }
} 