UNPKG

@iriseller/mcp-server

Version:

Model Context Protocol (MCP) server providing access to IRISeller's AI sales intelligence platform with 7 AI agents, multi-CRM integration, advanced sales workflows, email automation, Rosa demo functionality with action scoring, DNC compliance checking, G

3,930 lines • 199 kB
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
import { WebSearchService } from '../services/web-search-service.js';
import { StockDataService } from '../services/stock-data-service.js';
import { EmailToolHandlers } from './email-handlers.js';
import { CRMQuerySchema, CompanyResearchRequestSchema, PersonalizationRequestSchema, AgentExecutionRequestSchema, WorkflowExecutionRequestSchema } from '../types/index.js';
export class ToolHandlers {
    apiService;
    webSearchService;
    stockDataService;
    emailHandlers;
    userToken; // Store user token for CRM operations
    // Debug logging utilities that respect debug flag
    debugLog = (message, ...args) => {
        if (this.apiService.config?.debug) {
            console.error(message, ...args);
        }
    };
    errorLog = (message, ...args) => {
        // Always log errors to stderr (for critical issues only)
        console.error(message, ...args);
    };
    constructor(apiService) {
        this.apiService = apiService;
        this.webSearchService = new WebSearchService();
        this.stockDataService = new StockDataService();
        this.emailHandlers = new EmailToolHandlers(apiService);
    }
    // Method to set user token for authenticated operations
    setUserToken(token) {
        this.userToken = token;
        this.apiService.setUserToken(token);
    }
    async handleToolCall(request) {
        try {
            switch (request.params.name) {
                case 'qualify_lead':
                    return await this.handleQualifyLead(request);
                case 'query_crm':
                    return await this.handleQueryCRM(request);
                case 'research_company':
                    return await this.handleResearchCompany(request);
                case 'execute_agent':
                    return await this.handleExecuteAgent(request);
                case 'execute_workflow':
                    return await this.handleExecuteWorkflow(request);
                case 'personalize_outreach':
                    return await this.handlePersonalizeOutreach(request);
                case 'get_agent_results':
                    return await this.handleGetAgentResults(request);
                case 'wait_for_agent_completion':
                    return await this.handleWaitForAgentCompletion(request);
                case 'forecast_sales':
                    return await this.handleForecastSales(request);
                case 'list_agents':
                    return await this.handleListAgents(request);
                case 'list_workflows':
                    return await this.handleListWorkflows(request);
                case 'health_check':
                    return await this.handleHealthCheck(request);
                case 'web_search':
                    return await this.handleWebSearch(request);
                case 'get_stock_data':
                    return await this.handleGetStockData(request);
                case 'get_maps_location':
                    return await this.handleGoogleMapsLookup(request);
                case 'check_dnc_compliance':
                    return await this.handleDNCComplianceCheck(request);
                case 'rosa_daily_briefing':
                    return await this.handleRosaDailyBriefing(request);
                case 'prepare_meeting':
                    return await this.handleMeetingPrep(request);
                case 'priority_tasks':
                    return await this.handlePriorityTasks(request);
                // Email Integration Tools
                case 'email_auto_respond':
                    return await this.handleEmailAutoRespond(request);
                case 'analyze_email_intent':
                    return await this.handleEmailIntentAnalysis(request);
                case 'compile_sales_info':
                    return await this.handleCompileSalesInfo(request);
                case 'generate_email_response':
                    return await this.handleGenerateEmailResponse(request);
                case 'schedule_email_followup':
                    return await this.handleScheduleEmailFollowup(request);
                case 'get_email_analytics':
                    return await this.handleEmailAnalytics(request);
                // New Email Detection & Sending Tools
                case 'detect_emails':
                    return await this.handleDetectEmails(request);
                case 'send_email':
                    return await this.handleSendEmail(request);
                case 'get_email_status':
                    return await this.handleGetEmailStatus(request);
                case 'manage_email_templates':
                    return await this.handleManageEmailTemplates(request);
                case 'manage_email_campaigns':
                    return await this.handleManageEmailCampaigns(request);
                default:
                    throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
            }
        }
        catch (error) {
            if (error instanceof McpError) {
                throw error;
            }
            this.errorLog(`[MCP] Tool execution error for ${request.params.name}:`, error);
            throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleQualifyLead(request) {
        const args = request.params.arguments || {};
        // Validate required fields
        if (!args.contact_name || !args.company_name) {
            throw new McpError(ErrorCode.InvalidParams, 'contact_name and company_name are required');
        }
        try {
            // For now, skip database creation and let CrewAI handle execution tracking
            // The email automation system should work with CrewAI execution_ids 
            this.debugLog(`[MCP] Processing qualify_lead for: ${args.contact_name} at ${args.company_name}`);
            const result = await this.apiService.qualifyLead({
                contact_name: args.contact_name,
                company_name: args.company_name,
                title: args.title,
                industry: args.industry,
                email: args.email,
                phone: args.phone,
                additional_context: args.additional_context
            });
            // Handle both success and error cases
            if (result.status === 'error') {
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify({
                                tool: 'qualify_lead',
                                execution_id: result.execution_id,
                                status: 'error',
                                error: result.error || 'Lead qualification failed',
                                contact: args.contact_name,
                                company: args.company_name,
                                timestamp: new Date().toISOString()
                            }, null, 2)
                        }
                    ]
                };
            }
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'qualify_lead',
                            execution_id: result.execution_id, // Use CrewAI execution_id for follow-up tracking
                            status: result.status,
                            lead_qualification: {
                                contact: args.contact_name,
                                company: args.company_name,
                                qualification_score: result.results?.enhanced_qualification?.enhanced_score || result.results?.bant_analysis?.overall_score || 0,
                                qualification_status: result.results?.enhanced_qualification?.qualification_status || result.results?.bant_analysis?.qualification_status || 'Unknown',
                                bant_breakdown: result.results?.bant_analysis || {},
                                research_insights: result.results?.research_insights || {},
                                recommendations: result.results?.recommendations || [],
                                next_actions: result.results?.next_actions || [],
                                revenue_potential: result.results?.revenue_potential || {}
                            },
                            execution_time: result.execution_time,
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Lead qualification error:', error);
            throw new McpError(ErrorCode.InternalError, `Lead qualification failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleQueryCRM(request) {
        const args = request.params.arguments || {};
        // Validate and parse the query
        const parseResult = CRMQuerySchema.safeParse(args);
        if (!parseResult.success) {
            throw new McpError(ErrorCode.InvalidParams, `Invalid CRM query parameters: ${parseResult.error.message}`);
        }
        // Smart sorting logic: For leads queries without explicit sort_by OR when sort_by is 'score', default to value-based sorting
        const queryData = { ...parseResult.data };
        this.debugLog(`[MCP] DEBUG: Raw args:`, args);
        this.debugLog(`[MCP] DEBUG: Parsed queryData:`, queryData);
        this.debugLog(`[MCP] DEBUG: entity_type=${queryData.entity_type}, sort_by=${queryData.sort_by}`);
        const needsRosaScoring = queryData.entity_type === 'leads' && (!queryData.sort_by || queryData.sort_by === 'score' || queryData.sort_by === 'action_score');
        this.debugLog(`[MCP] DEBUG: needsRosaScoring=${needsRosaScoring}`);
        let originalLimit = queryData.limit;
        if (needsRosaScoring) {
            // For Rosa-style scoring, fetch more records to sort client-side with enhanced action scores
            // since CRM Connect Gateway may not support advanced action score sorting
            queryData.limit = Math.max(100, queryData.limit || 10); // Fetch at least 100 records
            this.debugLog(`[MCP] Fetching ${queryData.limit} leads for Rosa-style action scoring (original limit: ${originalLimit})`);
            this.debugLog(`[MCP] DEBUG: needsRosaScoring=true, entity_type=${queryData.entity_type}, sort_by=${parseResult.data.sort_by}`);
            // Remove sort_by to let CRM return records in default order
            const modifiedQuery = { ...queryData };
            delete modifiedQuery.sort_by;
            delete modifiedQuery.sort_order;
            Object.assign(queryData, modifiedQuery);
        }
        try {
            // Pass user token to CRM query for user-specific API key access
            const result = await this.apiService.queryCRM(queryData, this.userToken);
            let finalData = result.data;
            let finalQuery = queryData;
            // Check if data was owner-filtered (security update)
            const isOwnerFiltered = result.metadata?.ownerFiltered === true;
            if (isOwnerFiltered) {
                this.debugLog(`[MCP] CRM data is owner-filtered for security compliance`);
            }
            // Apply client-side sorting if needed
            if (needsRosaScoring && result.success && Array.isArray(result.data)) {
                this.debugLog(`[MCP] Applying Rosa-style action scoring to ${result.data.length} leads`);
                // Calculate Rosa-style action scores for each lead
                const scoredData = result.data.map((lead) => ({
                    ...lead,
                    actionScore: this.calculateRosaActionScore(lead),
                    rosaCategory: this.determineRosaCategory(lead)
                }));
                // Sort by action score (descending)
                const sortedData = scoredData.sort((a, b) => {
                    return b.actionScore - a.actionScore; // Descending order
                });
                // Limit to original requested amount
                finalData = sortedData.slice(0, originalLimit || 10);
                // Update query info to reflect what we actually did
                finalQuery = {
                    ...queryData,
                    sort_by: 'action_score',
                    sort_order: 'desc',
                    limit: originalLimit
                };
                const topLead = finalData[0];
                const leadName = topLead ? `${topLead.FirstName || topLead.firstName || topLead.name || 'Unknown'} ${topLead.LastName || topLead.lastName || ''}`.trim() : 'None';
                const actionScore = topLead ? topLead.actionScore : 0;
                this.debugLog(`[MCP] Rosa action scoring complete. Top lead: ${leadName} (Action Score: ${actionScore})`);
                this.debugLog(`[MCP] DEBUG: Sorted ${finalData.length} leads by action score. Top 3: ${finalData.slice(0, 3).map((l) => `${l.FirstName} ${l.LastName} (${l.actionScore})`).join(', ')}`);
            }
            // Enhanced response with security metadata
            const responseData = {
                tool: 'query_crm',
                query: finalQuery,
                success: result.success,
                data: finalData,
                count: Array.isArray(finalData) ? finalData.length : 0,
                message: result.message,
                error: result.error,
                metadata: {
                    ...result.metadata,
                    ownerFiltered: isOwnerFiltered,
                    securityCompliant: true
                },
                timestamp: new Date().toISOString()
            };
            // Add security notice for opportunity queries
            let responseText = JSON.stringify(responseData, null, 2);
            if (queryData.entity_type === 'opportunities' && isOwnerFiltered) {
                responseText += '\n\nšŸ”’ Security Notice: Results are filtered to show only opportunities you own for data security and compliance.';
            }
            return {
                content: [
                    {
                        type: 'text',
                        text: responseText
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] CRM query error:', error);
            // Enhanced error handling for owner filtering scenarios
            let errorMessage = error instanceof Error ? error.message : 'Unknown error';
            // Check if this is a 404 error for opportunities (likely due to owner filtering)
            if (queryData.entity_type === 'opportunities' && errorMessage.includes('404')) {
                errorMessage = `No opportunities found that you own. This is due to security filtering that ensures you only see your own opportunities.`;
            }
            else if (queryData.entity_type === 'opportunities' && errorMessage.includes('403')) {
                errorMessage = `Access denied to opportunities. Please check your CRM connection permissions.`;
            }
            throw new McpError(ErrorCode.InternalError, `CRM query failed: ${errorMessage}`);
        }
    }
    async handleResearchCompany(request) {
        const args = request.params.arguments || {};
        // Validate and parse the research request
        const parseResult = CompanyResearchRequestSchema.safeParse(args);
        if (!parseResult.success) {
            throw new McpError(ErrorCode.InvalidParams, `Invalid company research parameters: ${parseResult.error.message}`);
        }
        try {
            const result = await this.apiService.researchCompany(parseResult.data);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'research_company',
                            company: parseResult.data.company_name,
                            research_depth: parseResult.data.research_depth,
                            success: result.success,
                            research_data: result.data,
                            message: result.message,
                            error: result.error,
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Company research error:', error);
            throw new McpError(ErrorCode.InternalError, `Company research failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleExecuteAgent(request) {
        const args = request.params.arguments || {};
        // Validate and parse the agent execution request
        const parseResult = AgentExecutionRequestSchema.safeParse(args);
        if (!parseResult.success) {
            throw new McpError(ErrorCode.InvalidParams, `Invalid agent execution parameters: ${parseResult.error.message}`);
        }
        try {
            // First validate that the agent exists
            const availableAgents = await this.apiService.getAvailableAgents();
            if (!availableAgents.success) {
                this.debugLog('[MCP] Could not validate agent availability, proceeding with execution');
            }
            else {
                const validAgents = availableAgents.data?.map((agent) => agent.name) || [];
                if (validAgents.length > 0 && !validAgents.includes(parseResult.data.agent_name)) {
                    throw new McpError(ErrorCode.InvalidParams, `Agent '${parseResult.data.agent_name}' not found. Available agents: ${validAgents.join(', ')}`);
                }
            }
            const result = await this.apiService.executeAgent(parseResult.data);
            // If the agent is running/pending, provide guidance on how to get results
            if (result.status === 'pending' || result.status === 'running') {
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify({
                                tool: 'execute_agent',
                                agent_name: result.agent_name,
                                execution_id: result.execution_id,
                                status: result.status,
                                results: result.results,
                                execution_time: result.execution_time,
                                error: result.error,
                                next_steps: {
                                    message: "Agent execution is running in the background. Use wait_for_agent_completion to get the final results.",
                                    recommended_action: `Use wait_for_agent_completion with execution_id: "${result.execution_id}" to wait for and receive the final results`,
                                    alternative_action: `Or use get_agent_results with execution_id: "${result.execution_id}" and wait_for_completion: true for shorter waits`,
                                    polling_suggestion: "You can also check periodically without waiting by using get_agent_results with wait_for_completion: false"
                                },
                                timestamp: new Date().toISOString()
                            }, null, 2)
                        }
                    ]
                };
            }
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'execute_agent',
                            agent_name: result.agent_name,
                            execution_id: result.execution_id,
                            status: result.status,
                            results: result.results,
                            execution_time: result.execution_time,
                            error: result.error,
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Agent execution error:', error);
            if (error instanceof McpError) {
                throw error;
            }
            throw new McpError(ErrorCode.InternalError, `Agent execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleExecuteWorkflow(request) {
        const args = request.params.arguments || {};
        // Validate and parse the workflow execution request
        const parseResult = WorkflowExecutionRequestSchema.safeParse(args);
        if (!parseResult.success) {
            throw new McpError(ErrorCode.InvalidParams, `Invalid workflow execution parameters: ${parseResult.error.message}`);
        }
        try {
            // First validate that the workflow exists
            const availableWorkflows = await this.apiService.getAvailableWorkflows();
            if (!availableWorkflows.success) {
                this.debugLog('[MCP] Could not validate workflow availability, proceeding with execution');
            }
            else {
                const validWorkflows = availableWorkflows.data?.map((workflow) => workflow.name) || [];
                if (validWorkflows.length > 0 && !validWorkflows.includes(parseResult.data.workflow_type)) {
                    throw new McpError(ErrorCode.InvalidParams, `Workflow '${parseResult.data.workflow_type}' not found. Available workflows: ${validWorkflows.join(', ')}`);
                }
            }
            const result = await this.apiService.executeWorkflow(parseResult.data);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'execute_workflow',
                            workflow_type: result.workflow_type,
                            workflow_id: result.workflow_id,
                            status: result.status,
                            results: result.results,
                            agents_executed: result.agents_executed,
                            execution_time: result.execution_time,
                            error: result.error,
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Workflow execution error:', error);
            if (error instanceof McpError) {
                throw error;
            }
            throw new McpError(ErrorCode.InternalError, `Workflow execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handlePersonalizeOutreach(request) {
        const args = request.params.arguments || {};
        // Validate and parse the personalization request
        const parseResult = PersonalizationRequestSchema.safeParse(args);
        if (!parseResult.success) {
            throw new McpError(ErrorCode.InvalidParams, `Invalid personalization parameters: ${parseResult.error.message}`);
        }
        try {
            const result = await this.apiService.personalizeOutreach(parseResult.data);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'personalize_outreach',
                            prospect: parseResult.data.prospect_data,
                            message_type: parseResult.data.message_type,
                            tone: parseResult.data.tone,
                            success: result.success,
                            personalized_content: result.data,
                            message: result.message,
                            error: result.error,
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Personalization error:', error);
            throw new McpError(ErrorCode.InternalError, `Personalization failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleGetAgentResults(request) {
        const args = request.params.arguments || {};
        if (!args.execution_id || typeof args.execution_id !== 'string') {
            throw new McpError(ErrorCode.InvalidParams, 'execution_id is required and must be a string');
        }
        const executionId = args.execution_id;
        const waitForCompletion = args.wait_for_completion || false;
        const timeoutSeconds = Math.min(Math.max(args.timeout_seconds || 30, 5), 300);
        try {
            let results = await this.apiService.getCompletedExecution(executionId);
            // If no results found and wait_for_completion is true, poll for results
            if (!results && waitForCompletion) {
                this.debugLog(`[MCP] Waiting for agent execution completion: ${executionId}`);
                const startTime = Date.now();
                const pollInterval = 2000; // 2 seconds
                while (!results && (Date.now() - startTime) < (timeoutSeconds * 1000)) {
                    await new Promise(resolve => setTimeout(resolve, pollInterval));
                    results = await this.apiService.getCompletedExecution(executionId);
                    if (results && (results.status === 'completed' || results.status === 'failed')) {
                        break;
                    }
                }
            }
            if (!results) {
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify({
                                tool: 'get_agent_results',
                                execution_id: executionId,
                                status: 'not_found',
                                message: 'Execution not found or still running. Use wait_for_completion=true to wait for results.',
                                timestamp: new Date().toISOString()
                            }, null, 2)
                        }
                    ]
                };
            }
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'get_agent_results',
                            execution_id: executionId,
                            status: results.status,
                            agent_name: results.agent_name || results.agentName,
                            results: results.results || results.result,
                            result_summary: results.result_summary || results.resultSummary,
                            confidence: results.confidence,
                            execution_time: results.execution_time || results.executionTime,
                            completed_at: results.completed_at || results.completedAt,
                            created_at: results.created_at || results.createdAt,
                            retrieved: results.retrieved,
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Get agent results error:', error);
            if (error instanceof McpError) {
                throw error;
            }
            throw new McpError(ErrorCode.InternalError, `Failed to get agent results: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleWaitForAgentCompletion(request) {
        const args = request.params.arguments || {};
        if (!args.execution_id || typeof args.execution_id !== 'string') {
            throw new McpError(ErrorCode.InvalidParams, 'execution_id is required and must be a string');
        }
        const executionId = args.execution_id;
        const timeoutSeconds = Math.min(Math.max(args.timeout_seconds || 120, 10), 600);
        const checkIntervalSeconds = Math.min(Math.max(args.check_interval_seconds || 5, 2), 30);
        try {
            this.debugLog(`[MCP] Waiting for agent execution completion: ${executionId} (timeout: ${timeoutSeconds}s, interval: ${checkIntervalSeconds}s)`);
            const startTime = Date.now();
            let results = null;
            let lastStatus = 'unknown';
            while ((Date.now() - startTime) < (timeoutSeconds * 1000)) {
                results = await this.apiService.getCompletedExecution(executionId);
                if (results) {
                    lastStatus = results.status;
                    if (results.status === 'completed') {
                        this.debugLog(`[MCP] Agent execution ${executionId} completed successfully`);
                        break;
                    }
                    else if (results.status === 'failed') {
                        this.debugLog(`[MCP] Agent execution ${executionId} failed`);
                        break;
                    }
                }
                // Wait before next check
                await new Promise(resolve => setTimeout(resolve, checkIntervalSeconds * 1000));
            }
            const waitTime = Math.round((Date.now() - startTime) / 1000);
            if (!results) {
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify({
                                tool: 'wait_for_agent_completion',
                                execution_id: executionId,
                                status: 'timeout',
                                message: `Agent execution not found or still running after ${waitTime} seconds. The execution may still be processing in the background.`,
                                wait_time_seconds: waitTime,
                                timeout_seconds: timeoutSeconds,
                                timestamp: new Date().toISOString()
                            }, null, 2)
                        }
                    ]
                };
            }
            if (results.status === 'completed') {
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify({
                                tool: 'wait_for_agent_completion',
                                execution_id: executionId,
                                status: 'completed',
                                agent_name: results.agent_name || results.agentName,
                                results: results.results || results.result,
                                result_summary: results.result_summary || results.resultSummary,
                                confidence: results.confidence,
                                execution_time: results.execution_time || results.executionTime,
                                wait_time_seconds: waitTime,
                                completed_at: results.completed_at || results.completedAt,
                                message: `Agent execution completed successfully after waiting ${waitTime} seconds.`,
                                timestamp: new Date().toISOString()
                            }, null, 2)
                        }
                    ]
                };
            }
            else {
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify({
                                tool: 'wait_for_agent_completion',
                                execution_id: executionId,
                                status: results.status,
                                agent_name: results.agent_name || results.agentName,
                                error: results.error || `Agent execution ${results.status}`,
                                wait_time_seconds: waitTime,
                                message: `Agent execution ${results.status} after waiting ${waitTime} seconds.`,
                                timestamp: new Date().toISOString()
                            }, null, 2)
                        }
                    ]
                };
            }
        }
        catch (error) {
            this.errorLog('[MCP] Wait for agent completion error:', error);
            if (error instanceof McpError) {
                throw error;
            }
            throw new McpError(ErrorCode.InternalError, `Failed to wait for agent completion: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleForecastSales(request) {
        const args = request.params.arguments || {};
        try {
            // Validate time_period format
            const timePeriod = args.time_period || '90d';
            const validTimePeriodRegex = /^(30d|90d|quarter|quarterly|annual|Q[1-4]|Q[1-4] \d{4}|FY \d{4}|FY\d{2})$/;
            if (!validTimePeriodRegex.test(timePeriod)) {
                throw new McpError(ErrorCode.InvalidParams, `Invalid time_period format: "${timePeriod}". Supported formats: 30d, 90d, quarter, quarterly, annual, Q1-Q4, Q1 2024, FY 2024, FY25, etc.`);
            }
            const result = await this.apiService.getForecast({
                time_period: timePeriod,
                include_scenarios: args.include_scenarios !== false,
                filters: args.filters || {}
            }, this.userToken);
            // Ensure we have a proper response structure
            const response = {
                tool: 'forecast_sales',
                time_period: timePeriod,
                success: result?.success ?? false,
                forecast_data: result?.data || null,
                message: result?.message || 'Forecast completed',
                error: result?.error || null,
                timestamp: new Date().toISOString()
            };
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Sales forecast error:', error);
            if (error instanceof McpError) {
                throw error;
            }
            throw new McpError(ErrorCode.InternalError, `Sales forecast failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleListAgents(request) {
        const args = request.params.arguments || {};
        try {
            const result = await this.apiService.getAvailableAgents();
            let healthInfo = null;
            if (args.include_status !== false) {
                try {
                    healthInfo = await this.apiService.checkHealth();
                }
                catch (error) {
                    this.debugLog('[MCP] Health check failed during list agents:', error);
                    healthInfo = { backend: false, crewai: false, crmConnect: false };
                }
            }
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'list_agents',
                            success: result.success,
                            agents: result.data,
                            health_status: healthInfo,
                            message: result.message,
                            error: result.error,
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] List agents error:', error);
            throw new McpError(ErrorCode.InternalError, `List agents failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleListWorkflows(request) {
        const args = request.params.arguments || {};
        try {
            const result = await this.apiService.getAvailableWorkflows();
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'list_workflows',
                            success: result.success,
                            workflows: result.data,
                            include_details: args.include_details !== false,
                            message: result.message,
                            error: result.error,
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] List workflows error:', error);
            throw new McpError(ErrorCode.InternalError, `List workflows failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleHealthCheck(request) {
        const args = request.params.arguments || {};
        try {
            const healthStatus = await this.apiService.checkHealth();
            const overallHealth = healthStatus.backend && healthStatus.crewai && healthStatus.crmConnect;
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'health_check',
                            overall_status: overallHealth ? 'healthy' : 'degraded',
                            services: {
                                backend: {
                                    status: healthStatus.backend ? 'healthy' : 'unhealthy',
                                    description: 'IRISeller Backend API',
                                    url: 'http://localhost:3001'
                                },
                                crewai: {
                                    status: healthStatus.crewai ? 'healthy' : 'unhealthy',
                                    description: 'CrewAI Agent Service',
                                    url: 'http://localhost:8001'
                                },
                                crm_connect: {
                                    status: healthStatus.crmConnect ? 'healthy' : 'unhealthy',
                                    description: 'CRM Connect Gateway',
                                    url: 'http://localhost:3001/api/crm-connect'
                                }
                            },
                            detailed: args.detailed === true,
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Health check error:', error);
            // Return partial health info even if check fails
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'health_check',
                            overall_status: 'error',
                            services: {
                                backend: { status: 'unknown', description: 'IRISeller Backend API' },
                                crewai: { status: 'unknown', description: 'CrewAI Agent Service' },
                                crm_connect: { status: 'unknown', description: 'CRM Connect Gateway' }
                            },
                            error: error instanceof Error ? error.message : 'Health check failed',
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
    }
    async handleWebSearch(request) {
        try {
            const args = request.params.arguments || {};
            // Validate required parameters
            if (!args.query || typeof args.query !== 'string') {
                throw new McpError(ErrorCode.InvalidParams, 'query parameter is required and must be a string');
            }
            // Check if web search is available
            if (!this.webSearchService.isAvailable()) {
                throw new McpError(ErrorCode.InternalError, 'Claude web search is not available. Please configure ANTHROPIC_API_KEY environment variable.');
            }
            // Prepare search request for Claude's web search
            const searchRequest = {
                query: args.query,
                max_uses: typeof args.max_uses === 'number' ? Math.min(Math.max(args.max_uses, 1), 5) : 3,
                allowed_domains: Array.isArray(args.allowed_domains) ? args.allowed_domains : undefined,
                blocked_domains: Array.isArray(args.blocked_domains) ? args.blocked_domains : undefined,
                user_location: args.user_location && typeof args.user_location === 'object' && 'type' in args.user_location && args.user_location.type === 'approximate'
                    ? args.user_location
                    : undefined
            };
            // Execute Claude web search
            const searchResult = await this.webSearchService.search(searchRequest);
            // Format results for MCP response
            const formattedResults = {
                tool: 'web_search',
                query: searchResult.query,
                provider: searchResult.provider,
                answer: searchResult.answer,
                results: searchResult.results,
                citations: searchResult.citations,
                timestamp: searchResult.timestamp,
                configuration: this.webSearchService.getConfiguration()
            };
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(formattedResults, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Web search error:', error);
            if (error instanceof McpError) {
                throw error;
            }
            // Return error response with configuration information
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'web_search',
                            error: error instanceof Error ? error.message : 'Web search failed',
                            configuration: this.webSearchService.getConfiguration(),
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
    }
    async handleGetStockData(request) {
        try {
            const args = request.params.arguments || {};
            // Validate required parameters
            if (!args.symbol || typeof args.symbol !== 'string') {
                throw new McpError(ErrorCode.InvalidParams, 'symbol parameter is required and must be a string');
            }
            // Check if stock data service is available
            if (!this.stockDataService.isAvailable()) {
                throw new McpError(ErrorCode.InternalError, 'Stock data service is not available. Please configure ANTHROPIC_API_KEY environment variable.');
            }
            // Prepare stock data request
            const stockRequest = {
                symbol: args.symbol,
                company_name: typeof args.company_name === 'string' ? args.company_name : undefined,
                include_history: typeof args.include_history === 'boolean' ? args.include_history : true,
                history_period: typeof args.history_period === 'string' &&
                    ['1d', '5d', '1m', '3m', '6m', '1y'].includes(args.history_period)
                    ? args.history_period : '3m'
            };
            // Execute stock data search
            const stockResult = await this.stockDataService.getStockData(stockRequest);
            // Format results for MCP response
            const formattedResults = {
                tool: 'get_stock_data',
                request: stockRequest,
                stock_data: stockResult,
                timestamp: new Date().toISOString(),
                configuration: this.stockDataService.getConfiguration()
            };
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(formattedResults, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Stock data handler error:', error);
            // Return error response with configuration information
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            tool: 'get_stock_data',
                            error: error instanceof Error ? error.message : 'Stock data fetch failed',
                            configuration: this.stockDataService.getConfiguration(),
                            timestamp: new Date().toISOString()
                        }, null, 2)
                    }
                ]
            };
        }
    }
    async handleGoogleMapsLookup(request) {
        try {
            const args = request.params.arguments || {};
            let targetAddress = args.address;
            let contactInfo = null;
            // If no direct address provided, lookup from CRM
            if (!targetAddress && (args.person_name || args.company_name)) {
                this.debugLog('[MCP] Looking up address from CRM for:', args.person_name || args.company_name);
                // Query CRM to find the contact/lead and get their address
                // Use a broader query since backend doesn't support all filters
                const crmQuery = {
                    entity_type: 'leads',
                    limit: 50, // Increase limit to get more leads
                    offset: 0,
                    filters: {}
                };
                // The backend only supports status, industry, state, and search filters
                // We'll get all leads and then filter client-side
                const crmResult = await this.apiService.queryCRM(crmQuery, this.userToken);
                this.debugLog('[MCP] CRM query result:', {
                    success: crmResult.success,
                    dataLength: Array.isArray(crmResult.data) ? crmResult.data.length : 'not array',
                    error: crmResult.error,
                    query: crmQuery
                });
                if (crmResult.success && Array.isArray(crmResult.data) && crmResult.data.length > 0) {
                    const leads = crmResult.data;
                    let lead = null;
                    // Search through the results to find the matching lead
                    if (args.person_name && typeof args.person_name === 'string') {
                        const nameParts = args.person_name.trim().toLowerCase().split(' ');
                        lead = leads.find((l) => {
                            const firstName = (l.FirstName || l.firstName || '').toLowerCase();
                            const lastName = (l.LastName || l.lastName || '').toLowerCase();
                            const fullName = `${firstName} ${lastName}`.trim();
                            const searchName = args.person_name.toLowerCase().trim();
                            // Check for exact full name match
                            if (fullName === searchName) {
                                return true;
                            }
                            // Check if all name parts match
                            if (nameParts.length >= 2) {
                                return firstName === nameParts[0] && lastName === nameParts.slice(1).join(' ');
                            }
                            else if (nameParts.length === 1) {
                                // Single name - check both first and last name
                                return firstName === nameParts[0] || lastName === nameParts[0];
                            }
                            return false;
                        });
                    }
                    // If not found by name, try company name
                    if (!lead && args.company_name && typeof args.company_name === 'string') {
                        const searchCompany = args.company_name.toLowerCase().trim();
                        lead = leads.find((l) => (l.Company || l.company || '').toLowerCase().trim() === searchCompany);
                    }
                    if (lead) {
                        this.debugLog('[MCP] Found matching lead:', lead.FirstName || lead.firstName, lead.LastName || lead.lastName, '- Company:', lead.Company || lead.company);
                        this.debugLog('[MCP] Found lead data:', {
                            id: lead.id,
                            company: lead.Company || lead.company || lead.accountName || lead.companyName,
                            addressFields: {
                                Street: lead.Street,
                                street: lead.street,
                                address: lead.address,
                                City: lead.City,
                                city: lead.city,
                                State: lead.State,
                                state: lead.state
                            }
                        });
                        contactInfo = this.extractLeadData({ leads: [lead] });
                        // Build address from CRM fields - try both uppercase (Salesforce) and lowercase (normalized) field names
                        const addressParts = [
                            lead.Street || lead.street || lead.address,
                            lead.City || lead.city,
                            lead.State || lead.state,
                            lead.PostalCode || lead.postalCode || lead.zip,
                            lead.Country || lead.country
                        ].filter(Boolean);
                        if (addressParts.length > 0) {
                            targetAddress = addressParts.join(', ');
                        }
                    }
                    else {
                        this.debugLog('[MCP] No matching lead found among', leads.length, 'results');
                    }
                }
                // Also try contacts if no leads found
                if (!targetAddress) {
                    crmQuery.entity_type = 'contacts';
                    const contactsResult = await this.apiService.queryCRM(crmQuery, this.userToken);
                    if (contactsResult.success && Array.isArray(contactsResult.data) && contactsResult.data.length > 0) {
                        const contacts = contactsResult.data;
                        let contact = null;
                        // Search through contacts for matching person/company
                        if (args.person_name && typeof args.person_name === 'string') {
                            const nameParts = args.person_name.trim().toLowerCase().split(' ');
                            contact = contacts.find((c) => {
                                const firstName = (c.FirstName || c.firstName || '').toLowerCase();
                                const lastName = (c.LastName || c.lastName || '').toLowerCase();
                                const fullName = `${firstName} ${lastName}`.trim();
                                const searchName = args.person_name.toLowerCase().trim();
                                // Check for exact full name match
                                if (fullName === searchName) {
                                    return true;
                                }
                                // Check if all name parts match
                                if (nameParts.length >= 2) {
                                    return firstName === nameParts[0] && lastName === nameParts.slice(1).join(' ');
                                }
                                else if (nameParts.length === 1) {
                                    return firstName === nameParts[0] || lastName === nameParts[0];
                                }
                                return false;
                            });
                        }
                        // If not found by name, try company name
                        if (!contact && args.company_name && typeof args.company_name === 'string') {
                            const searchCompany = args.company_name.toLowerCase().trim();
                            contact = contacts.find((c) => (c.AccountName || c.accountName || c.Company || c.company || '').toLowerCase().trim() === searchCompany);
                        }
                        if (contact) {
                            this.debugLog('[MCP] Found matching contact:', contact.FirstName || contact.firstName, contact.LastName || contact.lastName, '- Company:', contact.AccountName || contact.accountName || contact.Company || contact.company);
                            contactInfo = this.extractLeadData({ contacts: [contact] });
                            const addressParts = [
                                contact.MailingStreet || contact.Street || contact.street || contact.address,
                                contact.MailingCity || contact.City || contact.city,
                                contact.MailingState || contact.State || contact.state,
                                contact.MailingPostalCode || contact.PostalCode || contact.postalCode || contact.zip,
                                contact.MailingCountry || contact.Country || contact.country
                            ].filter(Boolean);
                            if (addressParts.length > 0) {
                                targetAddress = addressParts.join(', ');
                            }
                        }
                        else {
                            this.debugLog('[MCP] No matching contact found among', contacts.length, 'results');
                        }
                    }
                }
            }
            if (!targetAddress) {
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify({
                                tool: 'get_maps_location',
                                error: 'No address found. Please provide an address directly or ensure the person/company exists in CRM with address data.',
                                searched_for: {
                                    person_name: args.person_name,
                                    company_name: args.company_name
                                },
                                timestamp: new Date().toISOString()
                            }, null, 2)
                        }
                    ]
                };
            }
            // Return simplified result with just the address
            const result = {
                tool: 'get_maps_location',
                success: true,
                address: targetAddress,
                contact_info: contactInfo,
                rosa_response: `Here's the address for ${contactInfo?.company || args.company_name || 'the requested location'}: ${targetAddress}`,
                timestamp: new Date().toISOString()
            };
            this.debugLog('[MCP] Found address:', targetAddress);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Google Maps lookup error:', error);
            if (error instanceof McpError) {
                throw error;
            }
            throw new McpError(ErrorCode.InternalError, `Google Maps lookup failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleDNCComplianceCheck(request) {
        try {
            const args = request.params.arguments || {};
            let targetContact = null;
            let dncStatus = { cleared: false, reason: '' };
            // If no direct contact info provided, lookup from CRM
            if (args.person_name || args.company_name || args.phone_number || args.email) {
                this.debugLog('[MCP] Looking up contact in CRM for DNC check:', args.person_name || args.company_name);
                // Query CRM to find the lead - use a broader query since backend doesn't support all filters
                const crmQuery = {
                    entity_type: 'leads',
                    limit: 50, // Increase limit to get more leads
                    offset: 0,
                    filters: {}
                };
                // The backend only supports status, industry, state, and search filters
                // We'll get all leads and then filter client-side
                const crmResult = await this.apiService.queryCRM(crmQuery, this.userToken);
                if (crmResult.success && Array.isArray(crmResult.data) && crmResult.data.length > 0) {
                    // Now search through the results to find the matching lead
                    const leads = crmResult.data;
                    // Try to find exact match first
                    if (args.person_name && typeof args.person_name === 'string') {
                        const nameParts = args.person_name.trim().toLowerCase().split(' ');
                        targetContact = leads.find((lead) => {
                            const firstName = (lead.FirstName || lead.firstName || '').toLowerCase();
                            const lastName = (lead.LastName || lead.lastName || '').toLowerCase();
                            const fullName = `${firstName} ${lastName}`.trim();
                            const searchName = args.person_name.toLowerCase().trim();
                            // Check for exact full name match
                            if (fullName === searchName) {
                                return true;
                            }
                            // Check if all name parts match
                            if (nameParts.length >= 2) {
                                return firstName === nameParts[0] && lastName === nameParts.slice(1).join(' ');
                            }
                            else if (nameParts.length === 1) {
                                // Single name - check both first and last name
                                return firstName === nameParts[0] || lastName === nameParts[0];
                            }
                            return false;
                        });
                    }
                    // If not found by name, try other criteria
                    if (!targetContact && args.email && typeof args.email === 'string') {
                        const searchEmail = args.email.toLowerCase();
                        targetContact = leads.find((lead) => (lead.Email || lead.email || '').toLowerCase() === searchEmail);
                    }
                    if (!targetContact && args.phone_number && typeof args.phone_number === 'string') {
                        const cleanPhone = args.phone_number.replace(/\D/g, '');
                        targetContact = leads.find((lead) => {
                            const leadPhone = (lead.Phone || lead.phone || '').replace(/\D/g, '');
                            return leadPhone === cleanPhone;
                        });
                    }
                    if (!targetContact && args.company_name && typeof args.company_name === 'string') {
                        const searchCompany = args.company_name.toLowerCase();
                        targetContact = leads.find((lead) => (lead.Company || lead.company || '').toLowerCase() === searchCompany);
                    }
                    // Log what we found
                    if (targetContact) {
                        this.debugLog('[MCP] Found matching lead:', targetContact.FirstName || targetContact.firstName, targetContact.LastName || targetContact.lastName, '- Company:', targetContact.Company || targetContact.company);
                    }
                    else {
                        this.debugLog('[MCP] No matching lead found among', leads.length, 'results');
                    }
                }
            }
            if (!targetContact) {
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify({
                                tool: 'check_dnc_compliance',
                                error: 'Contact not found in CRM. Please provide valid person name, company name, phone number, or email.',
                                searched_for: {
                                    person_name: args.person_name,
                                    company_name: args.company_name,
                                    phone_number: args.phone_number,
                                    email: args.email
                                },
                                timestamp: new Date().toISOString()
                            }, null, 2)
                        }
                    ]
                };
            }
            // Perform DNC compliance check using Fax field as Y/N flag
            dncStatus = this.checkDNCComplianceFromFaxField(targetContact);
            const result = {
                tool: 'check_dnc_compliance',
                success: true,
                contact_info: {
                    name: targetContact.FirstName ? `${targetContact.FirstName} ${targetContact.LastName || ''}`.trim() : targetContact.Name,
                    company: targetContact.Company || targetContact.AccountName,
                    phone: targetContact.Phone,
                    email: targetContact.Email,
                    title: targetContact.Title,
                    fax_field_value: targetContact.Fax || targetContact.fax || '(empty)'
                },
                compliance_status: {
                    cleared_for_contact: dncStatus.cleared,
                    dnc_method: 'Fax field Y/N flag',
                    check_reason: dncStatus.reason,
                    recommended_action: dncStatus.cleared ? 'Contact is approved' : 'DO NOT CONTACT - DNC restriction active'
                },
                rosa_response: dncStatus.cleared
                    ? `Checking compliance… ${targetContact.FirstName || targetContact.Name || 'Contact'} is cleared for contact - no DNC/DNE restrictions found.`
                    : `āš ļø DNC ALERT: ${targetContact.FirstName || targetContact.Name || 'Contact'} is on the Do Not Call list (Fax field: ${targetContact.Fax}). Please do not contact.`,
                timestamp: new Date().toISOString()
            };
            this.debugLog('[MCP] DNC compliance check completed using Fax field:', targetContact.Fax, 'Status:', dncStatus.cleared);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] DNC compliance check error:', error);
            if (error instanceof McpError) {
                throw error;
            }
            throw new McpError(ErrorCode.InternalError, `DNC compliance check failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleRosaDailyBriefing(request) {
        try {
            const args = request.params.arguments || {};
            const topLeadsCount = typeof args.top_leads_count === 'number' ? args.top_leads_count : 5;
            const includeMeetings = args.include_meetings !== false;
            const includeTasks = args.include_tasks !== false;
            const greetingStyle = args.greeting_style || 'rosa';
            this.debugLog(`[MCP] Generating Rosa daily briefing - ${topLeadsCount} leads, meetings: ${includeMeetings}, tasks: ${includeTasks}`);
            // Step 1: Get top leads with Rosa action scoring
            const leadsResult = await this.apiService.queryCRM({
                entity_type: 'leads',
                sort_by: 'action_score',
                sort_order: 'desc',
                limit: Math.max(topLeadsCount, 20), // Get more for better sorting
                offset: 0
            }, this.userToken);
            let topLeads = [];
            let upcomingMeetings = [];
            let priorityTasks = [];
            if (leadsResult.success && Array.isArray(leadsResult.data)) {
                // Apply Rosa scoring (already done in queryCRM handler)
                topLeads = leadsResult.data.slice(0, topLeadsCount);
                // Extract meetings and tasks from all leads (not just top ones)
                if (includeMeetings) {
                    upcomingMeetings = this.extractUpcomingMeetings(leadsResult.data);
                    this.debugLog(`[MCP] Extracted ${upcomingMeetings.length} meetings from NextStep fields`);
                }
                if (includeTasks) {
                    // Try multiple approaches to get tasks
                    priorityTasks = await this.fetchPriorityTasksFromCRM();
                    // If CRM tasks API failed, try to get activities using the activities endpoint
                    if (priorityTasks.length === 0) {
                        priorityTasks = await this.fetchActivitiesFromCRM();
                    }
                    this.debugLog(`[MCP] Fetched ${priorityTasks.length} priority tasks from CRM`);
                }
            }
            // Step 2: Format Rosa-style briefing
            const briefing = this.formatRosaDailyBriefing({
                topLeads,
                upcomingMeetings,
                priorityTasks,
                greetingStyle,
                topLeadsCount
            });
            const briefingResult = {
                tool: 'rosa_daily_briefing',
                greeting_style: greetingStyle,
                daily_briefing: {
                    greeting: briefing.greeting,
                    summary: briefing.summary,
                    top_leads: briefing.topLeads,
                    upcoming_meetings: briefing.upcomingMeetings,
                    priority_tasks: briefing.priorityTasks,
                    rosa_insights: briefing.rosaInsights
                },
                data_sources: {
                    leads_analyzed: leadsResult.data?.length || 0,
                    meetings_found: upcomingMeetings.length,
                    tasks_identified: priorityTasks.length,
                    action_scoring_applied: true
                },
                timestamp: new Date().toISOString()
            };
            this.debugLog('[MCP] Rosa daily briefing completed successfully');
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(briefingResult, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Rosa daily briefing error:', error);
            if (error instanceof McpError) {
                throw error;
            }
            throw new McpError(ErrorCode.InternalError, `Rosa daily briefing failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleMeetingPrep(request) {
        try {
            const args = request.params.arguments || {};
            // Validate required parameters
            if (!args.person_name || typeof args.person_name !== 'string') {
                throw new McpError(ErrorCode.InvalidParams, 'person_name parameter is required and must be a string');
            }
            const personName = args.person_name.trim();
            const companyName = typeof args.company_name === 'string' ? args.company_name.trim() : undefined;
            const meetingContext = typeof args.meeting_context === 'string' ? args.meeting_context : 'business meeting';
            const researchDepth = typeof args.research_depth === 'string' &&
                ['basic', 'comprehensive', 'deep'].includes(args.research_depth)
                ? args.research_depth : 'comprehensive';
            const includeCompanyAnalysis = typeof args.include_company_analysis === 'boolean' ? args.include_company_analysis : true;
            const focusAreas = Array.isArray(args.focus_areas) ? args.focus_areas : [];
            this.debugLog(`[MCP] Preparing meeting notes for: ${personName}${companyName ? ` at ${companyName}` : ''}`);
            // Step 1: Use personalization agent to get enriched CRM data
            let crmData = null;
            try {
                this.debugLog('[MCP] Step 1: Using personalization agent for comprehensive CRM lookup...');
                const agentResponse = await this.apiService.executeAgent({
                    agent_name: 'personalization',
                    input_data: {
                        person_name: personName,
                        company_name: companyName,
                        meeting_context: meetingContext
                    },
                    options: {
                        timeout: 30,
                        priority: 'high',
                        include_research: true
                    }
                });
                if (agentResponse?.results?.research_results?.contact_info) {
                    crmData = agentResponse.results.research_results.contact_info;
                    this.debugLog(`[MCP] Found enriched CRM data for ${personName}: ${crmData.email}, ${crmData.phone}`);
                }
                else {
                    this.debugLog(`[MCP] No CRM data found via personalization agent for ${personName}`);
                    this.debugLog(`[MCP] Agent response structure:`, JSON.stringify(agentResponse, null, 2).substring(0, 500));
                }
            }
            catch (error) {
                this.debugLog('[MCP] CRM lookup via personalization agent failed:', error instanceof Error ? error.message : 'Unknown error');
            }
            // Step 2: Web Research (if comprehensive or deep)
            let webResearchData = null;
            if (researchDepth === 'comprehensive' || researchDepth === 'deep') {
                try {
                    this.debugLog('[MCP] Step 2: Conducting web research...');
                    const searchQueries = [
                        `"${personName}"${companyName ? ` "${companyName}"` : ''} professional background`,
                        ...(companyName ? [`"${companyName}" recent news developments 2024`] : []),
                        ...(includeCompanyAnalysis && companyName ? [`"${companyName}" industry challenges opportunities`] : []),
                        ...(focusAreas.length > 0 ? focusAreas.map(area => `"${companyName || personName}" ${area}`) : [])
                    ];
                    const webResults = await Promise.all(searchQueries.slice(0, researchDepth === 'deep' ? 4 : 2).map(async (query) => {
                        try {
                            const searchRequest = {
                                params: {
                                    name: 'web_search',
                                    arguments: {
                                        query,
                                        max_uses: 2,
                                        allowed_domains: ['linkedin.com', 'crunchbase.com', 'bloomberg.com', 'techcrunch.com', 'forbes.com']
                                    }
                                },
                                method: 'tools/call'
                            };
                            return await this.handleWebSearch(searchRequest);
                        }
                        catch (error) {
                            this.debugLog(`[MCP] Web search failed for query: ${query}`, error);
                            return null;
                        }
                    }));
                    webResearchData = webResults
                        .filter(result => result && result.content?.[0]?.text)
                        .map(result => JSON.parse(result.content[0].text));
                    this.debugLog(`[MCP] Completed ${webResearchData.length} web searches`);
                }
                catch (error) {
                    this.debugLog('[MCP] Web research failed:', error instanceof Error ? error.message : 'Unknown error');
                }
            }
            // Step 3: Advanced AI Synthesis using Agent Execution
            let meetingNotes = null;
            try {
                this.debugLog('[MCP] Step 3: Generating AI-powered meeting notes...');
                // Prepare comprehensive context for AI analysis
                const aiContext = {
                    person_name: personName,
                    company_name: companyName,
                    meeting_context: meetingContext,
                    crm_data: crmData,
                    web_research: webResearchData,
                    focus_areas: focusAreas
                };
                // Use the agent execution system for advanced AI synthesis
                const agentRequest = {
                    params: {
                        name: 'execute_agent',
                        arguments: {
                            agent_name: 'rosa_sdr',
                            input_data: {
                                contact_name: personName,
                                company_name: crmData?.leads?.[0]?.company || crmData?.leads?.[0]?.Company || crmData?.contacts?.[0]?.company || companyName || 'Unknown Company',
                                title: crmData?.leads?.[0]?.title || crmData?.leads?.[0]?.Title || crmData?.contacts?.[0]?.title || 'Unknown Title',
                                email: crmData?.leads?.[0]?.email || crmData?.leads?.[0]?.Email || crmData?.contacts?.[0]?.email || null,
                                phone: crmData?.leads?.[0]?.phone || crmData?.leads?.[0]?.Phone || crmData?.contacts?.[0]?.phone || null,
                                additional_context: `Meeting preparation request for ${meetingContext}. CRM Status: ${crmData?.leads?.[0]?.status || crmData?.leads?.[0]?.Status || 'Active'}. Research focus areas: ${focusAreas.join(', ') || 'General business discussion'}. Web research completed: ${webResearchData?.length || 0} sources.`
                            },
                            options: {
                                timeout: 45,
                                priority: 'high',
                                include_research: true
                            }
                        }
                    },
                    method: 'tools/call'
                };
                try {
                    const aiResult = await this.handleExecuteAgent(agentRequest);
                    if (aiResult.content?.[0]?.text) {
                        const aiData = JSON.parse(aiResult.content[0].text);
                        // Extract meeting-specific insights from ROSA's analysis
                        if (aiData.results || aiData.analysis) {
                            const analysis = aiData.results || aiData.analysis;
                            meetingNotes = this.formatMeetingPrepInsights(analysis, personName, meetingContext);
                        }
                        else {
                            meetingNotes = `AI Analysis: ${JSON.stringify(aiData).substring(0, 500)}...`;
                        }
                        this.debugLog('[MCP] Advanced AI synthesis completed via ROSA SDR agent');
                    }
                }
                catch (agentError) {
                    this.debugLog('[MCP] Agent-based AI synthesis failed, using fallback:', agentError instanceof Error ? agentError.message : 'Unknown error');
                    // Fallback to enhanced basic synthesis
                    meetingNotes = this.generateEnhancedMeetingInsights(crmData, webResearchData, personName, companyName, meetingContext, focusAreas);
                }
            }
            catch (error) {
                this.debugLog('[MCP] AI synthesis failed:', error instanceof Error ? error.message : 'Unknown error');
                // Final fallback
                meetingNotes = this.generateEnhancedMeetingInsights(crmData, webResearchData, personName, companyName, meetingContext, focusAreas);
            }
            // Step 4: Format comprehensive response
            const meetingPrepResults = {
                tool: 'prepare_meeting',
                request: {
                    person_name: personName,
                    company_name: companyName,
                    meeting_context: meetingContext,
                    research_depth: researchDepth
                },
                meeting_preparation: {
                    executive_summary: this.generateExecutiveSummary(crmData, webResearchData, personName, companyName),
                    person_profile: this.extractPersonProfile(crmData, webResearchData, personName),
                    company_overview: companyName && includeCompanyAnalysis ? this.extractCompanyOverview(crmData, webResearchData, companyName) : null,
                    conversation_starters: this.generateConversationStarters(crmData, webResearchData, meetingContext),
                    potential_pain_points: this.identifyPainPoints(crmData, webResearchData, companyName),
                    meeting_objectives: this.suggestMeetingObjectives(meetingContext, crmData),
                    recommended_actions: this.generateActionItems(crmData, meetingContext),
                    ai_generated_notes: meetingNotes
                },
                data_sources: {
                    crm_data_found: crmData ? 1 : 0,
                    crm_provider: crmData?.crmProvider || 'Unknown',
                    web_searches_completed: webResearchData?.length || 0,
                    research_depth: researchDepth,
                    ai_synthesis_used: !!meetingNotes,
                    personalization_agent_used: true
                },
                timestamp: new Date().toISOString()
            };
            this.debugLog('[MCP] Meeting preparation completed successfully');
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(meetingPrepResults, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Meeting prep error:', error);
            if (error instanceof McpError) {
                throw error;
            }
            throw new McpError(ErrorCode.InternalError, `Meeting preparation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handlePriorityTasks(request) {
        try {
            const args = request.params.arguments || {};
            // Set defaults
            const taskTypes = Array.isArray(args.task_types) ? args.task_types : ['calls', 'emails', 'meetings', 'follow_ups'];
            const priorityLevel = typeof args.priority_level === 'string' ? args.priority_level : 'all';
            const limit = typeof args.limit === 'number' ? Math.min(Math.max(args.limit, 1), 50) : 10;
            const timeFrame = typeof args.time_frame === 'string' ? args.time_frame : 'all';
            const sortBy = typeof args.sort_by === 'string' ? args.sort_by : 'action_score';
            this.debugLog(`[MCP] Getting priority tasks: types=${taskTypes.join(',')}, priority=${priorityLevel}, limit=${limit}`);
            // Use the existing CRM tasks API to get real tasks
            const crmTasksQuery = {
                limit: Math.min(limit * 2, 100), // Fetch more to filter client-side
                offset: 0,
                sort_by: 'priority',
                sort_order: 'desc'
            };
            // Add priority filter if not 'all'
            if (priorityLevel !== 'all') {
                crmTasksQuery.priority = priorityLevel;
            }
            const crmResult = await this.apiService.queryCRMTasks(crmTasksQuery, this.userToken);
            if (!crmResult.success) {
                this.debugLog(`[MCP] CRM tasks API failed: ${crmResult.error}, trying fallback approach`);
                // Fallback to the existing method that extracts tasks from leads
                return await this.handlePriorityTasksFallback(args);
            }
            if (!Array.isArray(crmResult.data)) {
                throw new Error('CRM tasks API returned invalid data format');
            }
            this.debugLog(`[MCP] Retrieved ${crmResult.data.length} tasks from CRM Connect API`);
            // Filter and process the real CRM tasks
            let filteredTasks = crmResult.data
                .filter(task => this.filterCRMTaskByType(task, taskTypes))
                .filter(task => this.filterCRMTaskByTimeFrame(task, timeFrame))
                .map(task => this.normalizeCRMTask(task));
            // Sort tasks based on the requested sort method
            filteredTasks = filteredTasks.sort((a, b) => {
                switch (sortBy) {
                    case 'priority':
                        return this.comparePriority(b.priority, a.priority);
                    case 'due_date':
                        return new Date(a.due_date || '9999-12-31').getTime() - new Date(b.due_date || '9999-12-31').getTime();
                    case 'created_date':
                        return new Date(b.created_date || '1900-01-01').getTime() - new Date(a.created_date || '1900-01-01').getTime();
                    case 'action_score':
                    default:
                        return (b.action_score || 0) - (a.action_score || 0);
                }
            }).slice(0, limit);
            const priorityTasksResult = {
                tool: 'priority_tasks',
                request: {
                    task_types: taskTypes,
                    priority_level: priorityLevel,
                    limit: limit,
                    time_frame: timeFrame,
                    sort_by: sortBy
                },
                priority_tasks: filteredTasks,
                summary: {
                    total_tasks_found: crmResult.data.length,
                    tasks_returned: filteredTasks.length,
                    high_priority_count: filteredTasks.filter(t => (t.priority || '').toLowerCase() === 'high').length,
                    medium_priority_count: filteredTasks.filter(t => (t.priority || '').toLowerCase() === 'medium').length,
                    low_priority_count: filteredTasks.filter(t => (t.priority || '').toLowerCase() === 'low').length,
                    task_type_breakdown: this.getCRMTaskTypeBreakdown(filteredTasks)
                },
                insights: this.generateCRMTaskInsights(filteredTasks),
                data_source: 'crm_connect_tasks_api',
                timestamp: new Date().toISOString()
            };
            this.debugLog(`[MCP] Priority tasks completed: ${filteredTasks.length} tasks returned from CRM Connect API`);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(priorityTasksResult, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('[MCP] Priority tasks error:', error);
            if (error instanceof McpError) {
                throw error;
            }
            throw new McpError(ErrorCode.InternalError, `Priority tasks failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    // Fallback method that uses the old approach of parsing lead fields
    async handlePriorityTasksFallback(args) {
        const taskTypes = Array.isArray(args.task_types) ? args.task_types : ['calls', 'emails', 'meetings', 'follow_ups'];
        const priorityLevel = typeof args.priority_level === 'string' ? args.priority_level : 'all';
        const limit = typeof args.limit === 'number' ? Math.min(Math.max(args.limit, 1), 50) : 10;
        const timeFrame = typeof args.time_frame === 'string' ? args.time_frame : 'all';
        const sortBy = typeof args.sort_by === 'string' ? args.sort_by : 'action_score';
        this.debugLog(`[MCP] Using fallback task extraction from lead fields`);
        // Query leads to extract task information from CRM fields
        const crmQuery = {
            entity_type: 'leads',
            limit: Math.min(limit * 3, 100),
            offset: 0,
            sort_by: 'created_date',
            sort_order: 'desc'
        };
        const crmResult = await this.apiService.queryCRM(crmQuery, this.userToken);
        if (!crmResult.success || !Array.isArray(crmResult.data)) {
            throw new Error('Failed to fetch CRM data for fallback priority tasks');
        }
        // Extract tasks from CRM data using NextStep, Description, and other fields
        const extractedTasks = this.extractTasksFromCRMData(crmResult.data, taskTypes, timeFrame);
        // Score and filter tasks based on priority and criteria
        const scoredTasks = extractedTasks
            .map(task => ({
            ...task,
            actionScore: this.calculateTaskActionScore(task),
            priorityScore: this.calculateTaskPriorityScore(task)
        }))
            .filter(task => this.filterTaskByPriority(task, priorityLevel))
            .sort((a, b) => {
            switch (sortBy) {
                case 'priority':
                    return b.priorityScore - a.priorityScore;
                case 'due_date':
                    return new Date(a.dueDate || '9999-12-31').getTime() - new Date(b.dueDate || '9999-12-31').getTime();
                case 'created_date':
                    return new Date(b.createdDate || '1900-01-01').getTime() - new Date(a.createdDate || '1900-01-01').getTime();
                case 'action_score':
                default:
                    return b.actionScore - a.actionScore;
            }
        })
            .slice(0, limit);
        const priorityTasksResult = {
            tool: 'priority_tasks',
            request: {
                task_types: taskTypes,
                priority_level: priorityLevel,
                limit: limit,
                time_frame: timeFrame,
                sort_by: sortBy
            },
            priority_tasks: scoredTasks,
            summary: {
                total_tasks_found: extractedTasks.length,
                tasks_returned: scoredTasks.length,
                high_priority_count: scoredTasks.filter(t => t.priority === 'high').length,
                medium_priority_count: scoredTasks.filter(t => t.priority === 'medium').length,
                low_priority_count: scoredTasks.filter(t => t.priority === 'low').length,
                task_type_breakdown: this.getTaskTypeBreakdown(scoredTasks)
            },
            insights: this.generateTaskInsights(scoredTasks),
            data_source: 'crm_lead_fields_fallback',
            timestamp: new Date().toISOString()
        };
        return {
            content: [
                {
                    type: 'text',
                    text: JSON.stringify(priorityTasksResult, null, 2)
                }
            ]
        };
    }
    // Helper methods for CRM tasks
    filterCRMTaskByType(task, allowedTypes) {
        if (allowedTypes.includes('all'))
            return true;
        const taskType = this.determineCRMTaskType(task);
        return allowedTypes.includes(taskType);
    }
    determineCRMTaskType(task) {
        const subject = (task.subject || task.Subject || '').toLowerCase();
        const description = (task.description || task.Description || '').toLowerCase();
        const type = (task.type || task.Type || task.task_type || '').toLowerCase();
        // Check explicit type field first
        if (type.includes('call'))
            return 'calls';
        if (type.includes('email'))
            return 'emails';
        if (type.includes('meeting'))
            return 'meetings';
        if (type.includes('follow'))
            return 'follow_ups';
        if (type.includes('demo'))
            return 'demos';
        if (type.includes('proposal'))
            return 'proposals';
        // Check subject and description
        const text = `${subject} ${description}`;
        if (text.includes('call') || text.includes('phone'))
            return 'calls';
        if (text.includes('email') || text.includes('send'))
            return 'emails';
        if (text.includes('meeting') || text.includes('demo'))
            return 'meetings';
        if (text.includes('follow') || text.includes('check'))
            return 'follow_ups';
        if (text.includes('proposal') || text.includes('quote'))
            return 'proposals';
        return 'follow_ups'; // Default fallback
    }
    filterCRMTaskByTimeFrame(task, timeFrame) {
        if (timeFrame === 'all')
            return true;
        const dueDate = task.due_date || task.Due_Date || task.ActivityDate || task.activity_date;
        if (!dueDate)
            return timeFrame === 'all';
        const taskDate = new Date(dueDate);
        const now = new Date();
        switch (timeFrame) {
            case 'today':
                return taskDate.toDateString() === now.toDateString();
            case 'tomorrow':
                const tomorrow = new Date(now);
                tomorrow.setDate(tomorrow.getDate() + 1);
                return taskDate.toDateString() === tomorrow.toDateString();
            case 'this_week':
                const startOfWeek = new Date(now);
                startOfWeek.setDate(now.getDate() - now.getDay());
                const endOfWeek = new Date(startOfWeek);
                endOfWeek.setDate(startOfWeek.getDate() + 6);
                return taskDate >= startOfWeek && taskDate <= endOfWeek;
            case 'next_week':
                const nextWeekStart = new Date(now);
                nextWeekStart.setDate(now.getDate() + (7 - now.getDay()));
                const nextWeekEnd = new Date(nextWeekStart);
                nextWeekEnd.setDate(nextWeekStart.getDate() + 6);
                return taskDate >= nextWeekStart && taskDate <= nextWeekEnd;
            case 'this_month':
                return taskDate.getMonth() === now.getMonth() && taskDate.getFullYear() === now.getFullYear();
            default:
                return true;
        }
    }
    normalizeCRMTask(task) {
        return {
            id: task.id || task.Id || task.task_id,
            subject: task.subject || task.Subject || task.name || 'Untitled Task',
            description: task.description || task.Description || '',
            priority: this.normalizePriority(task.priority || task.Priority || task.Importance || 'Normal'),
            status: task.status || task.Status || task.TaskStatus || 'Not Started',
            type: this.determineCRMTaskType(task),
            due_date: task.due_date || task.Due_Date || task.ActivityDate || task.activity_date,
            created_date: task.created_date || task.CreatedDate || task.created_at,
            owner: task.owner || task.Owner || task.assigned_to || task.AssignedTo,
            contact: {
                name: task.contact_name || task.ContactName || task.WhoId || 'Unknown Contact',
                company: task.account_name || task.AccountName || task.company || 'Unknown Company',
                id: task.contact_id || task.ContactId || task.who_id
            },
            account: {
                name: task.account_name || task.AccountName || task.company,
                id: task.account_id || task.AccountId || task.what_id
            },
            action_score: this.calculateCRMTaskActionScore(task),
            crm_source: task.crm_source || task.source || 'unknown',
            original_data: task
        };
    }
    normalizePriority(priority) {
        const p = priority.toLowerCase();
        if (p.includes('high') || p.includes('urgent') || p.includes('critical'))
            return 'high';
        if (p.includes('low') || p.includes('minor'))
            return 'low';
        return 'medium';
    }
    comparePriority(a, b) {
        const priorityOrder = { 'high': 3, 'medium': 2, 'low': 1 };
        return (priorityOrder[a.toLowerCase()] || 2) - (priorityOrder[b.toLowerCase()] || 2);
    }
    calculateCRMTaskActionScore(task) {
        let score = 0;
        // Priority scoring
        const priority = (task.priority || task.Priority || '').toLowerCase();
        if (priority.includes('high') || priority.includes('urgent'))
            score += 50;
        else if (priority.includes('medium') || priority.includes('normal'))
            score += 30;
        else if (priority.includes('low'))
            score += 10;
        else
            score += 20; // Default
        // Due date urgency
        const dueDate = task.due_date || task.Due_Date || task.ActivityDate;
        if (dueDate) {
            const due = new Date(dueDate);
            const now = new Date();
            const daysUntilDue = Math.ceil((due.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
            if (daysUntilDue <= 0)
                score += 25; // Overdue
            else if (daysUntilDue <= 1)
                score += 20; // Due today/tomorrow
            else if (daysUntilDue <= 3)
                score += 15; // Due this week
            else if (daysUntilDue <= 7)
                score += 10; // Due next week
        }
        // Status scoring
        const status = (task.status || task.Status || '').toLowerCase();
        if (status.includes('in progress') || status.includes('started'))
            score += 15;
        else if (status.includes('waiting') || status.includes('deferred'))
            score += 5;
        else if (status.includes('completed'))
            score -= 10; // Lower priority for completed
        return Math.max(0, score);
    }
    getCRMTaskTypeBreakdown(tasks) {
        const breakdown = {};
        tasks.forEach(task => {
            const type = task.type || 'unknown';
            breakdown[type] = (breakdown[type] || 0) + 1;
        });
        return breakdown;
    }
    generateCRMTaskInsights(tasks) {
        const insights = [];
        if (tasks.length === 0) {
            insights.push("No priority tasks found matching your criteria.");
            return insights;
        }
        const highPriorityCount = tasks.filter(t => (t.priority || '').toLowerCase() === 'high').length;
        const overdueCount = tasks.filter(t => {
            if (!t.due_date)
                return false;
            return new Date(t.due_date) < new Date();
        }).length;
        if (highPriorityCount > 0) {
            insights.push(`You have ${highPriorityCount} high-priority tasks requiring immediate attention.`);
        }
        if (overdueCount > 0) {
            insights.push(`${overdueCount} tasks are overdue and need urgent action.`);
        }
        const taskTypes = [...new Set(tasks.map(t => t.type))];
        if (taskTypes.length > 1) {
            insights.push(`Your tasks span ${taskTypes.length} different activity types: ${taskTypes.join(', ')}.`);
        }
        const topTask = tasks[0];
        if (topTask) {
            insights.push(`Your highest priority task is: "${topTask.subject}" for ${topTask.contact.company}.`);
        }
        // Add CRM source insights
        const crmSources = [...new Set(tasks.map(t => t.crm_source).filter(Boolean))];
        if (crmSources.length > 1) {
            insights.push(`Tasks are coming from ${crmSources.length} CRM systems: ${crmSources.join(', ')}.`);
        }
        return insights;
    }
    // Helper methods for priority tasks (fallback)
    extractTasksFromCRMData(crmData, taskTypes, timeFrame) {
        const tasks = [];
        const now = new Date();
        crmData.forEach((record) => {
            // Extract tasks from NextStep field (common task field)
            const nextStep = record.NextStep || record.next_step || record.Description || record.description || '';
            const company = record.Company || record.company_name || record.company || 'Unknown Company';
            const contactName = `${record.FirstName || record.first_name || record.firstName || ''} ${record.LastName || record.last_name || record.lastName || ''}`.trim();
            const email = record.Email || record.email || '';
            const phone = record.Phone || record.phone || '';
            const status = record.Status || record.status || 'Active';
            const createdDate = record.CreatedDate || record.created_date || record.createdAt || now.toISOString();
            if (nextStep && nextStep.trim()) {
                // Parse task information from NextStep field
                const taskInfo = this.parseTaskFromText(nextStep, taskTypes);
                if (taskInfo.type) {
                    const task = {
                        id: `task_${record.Id || record.id || Math.random().toString(36).substr(2, 9)}`,
                        type: taskInfo.type,
                        title: taskInfo.title || nextStep.substring(0, 50) + (nextStep.length > 50 ? '...' : ''),
                        description: nextStep,
                        priority: taskInfo.priority || this.inferTaskPriority(nextStep, record),
                        contact: {
                            name: contactName || 'Unknown Contact',
                            company: company,
                            email: email,
                            phone: phone
                        },
                        leadInfo: {
                            status: status,
                            id: record.Id || record.id,
                            revenue: record.AnnualRevenue || record.annual_revenue || 0
                        },
                        dueDate: taskInfo.dueDate,
                        createdDate: createdDate,
                        source: 'crm_nextstep'
                    };
                    // Filter by time frame
                    if (this.matchesTimeFrame(task, timeFrame)) {
                        tasks.push(task);
                    }
                }
            }
            // Also check for meeting-related tasks in other fields
            if (taskTypes.includes('meetings')) {
                const meetingFields = [record.MeetingNotes, record.meeting_notes, record.LastActivityDate, record.last_activity_date];
                meetingFields.forEach(field => {
                    if (field && typeof field === 'string' && field.toLowerCase().includes('meeting')) {
                        const meetingTask = {
                            id: `meeting_${record.Id || record.id || Math.random().toString(36).substr(2, 9)}`,
                            type: 'meetings',
                            title: `Follow up on meeting with ${contactName}`,
                            description: field,
                            priority: 'medium',
                            contact: {
                                name: contactName || 'Unknown Contact',
                                company: company,
                                email: email,
                                phone: phone
                            },
                            leadInfo: {
                                status: status,
                                id: record.Id || record.id,
                                revenue: record.AnnualRevenue || record.annual_revenue || 0
                            },
                            createdDate: createdDate,
                            source: 'crm_meeting_notes'
                        };
                        if (this.matchesTimeFrame(meetingTask, timeFrame)) {
                            tasks.push(meetingTask);
                        }
                    }
                });
            }
        });
        return tasks;
    }
    parseTaskFromText(text, allowedTypes) {
        const lowerText = text.toLowerCase();
        let type = null;
        let priority = null;
        let dueDate = null;
        let title = text.substring(0, 100);
        // Determine task type based on keywords
        if (allowedTypes.includes('calls') && (lowerText.includes('call') || lowerText.includes('phone'))) {
            type = 'calls';
        }
        else if (allowedTypes.includes('emails') && (lowerText.includes('email') || lowerText.includes('send') || lowerText.includes('follow up'))) {
            type = 'emails';
        }
        else if (allowedTypes.includes('meetings') && (lowerText.includes('meeting') || lowerText.includes('schedule') || lowerText.includes('demo'))) {
            type = 'meetings';
        }
        else if (allowedTypes.includes('follow_ups') && (lowerText.includes('follow') || lowerText.includes('check'))) {
            type = 'follow_ups';
        }
        else if (allowedTypes.includes('demos') && (lowerText.includes('demo') || lowerText.includes('presentation'))) {
            type = 'demos';
        }
        else if (allowedTypes.includes('proposals') && (lowerText.includes('proposal') || lowerText.includes('quote'))) {
            type = 'proposals';
        }
        else if (allowedTypes.includes('research') && (lowerText.includes('research') || lowerText.includes('investigate'))) {
            type = 'research';
        }
        // Determine priority based on keywords
        if (lowerText.includes('urgent') || lowerText.includes('asap') || lowerText.includes('immediate')) {
            priority = 'high';
        }
        else if (lowerText.includes('important') || lowerText.includes('priority')) {
            priority = 'high';
        }
        else if (lowerText.includes('when possible') || lowerText.includes('low priority')) {
            priority = 'low';
        }
        else {
            priority = 'medium';
        }
        // Try to extract due dates
        const datePatterns = [
            /(?:by|due|before)\s+(\d{1,2}\/\d{1,2}\/\d{4})/i,
            /(?:by|due|before)\s+(\d{4}-\d{2}-\d{2})/i,
            /(?:today|tomorrow|this week|next week)/i
        ];
        for (const pattern of datePatterns) {
            const match = text.match(pattern);
            if (match) {
                if (match[1]) {
                    dueDate = match[1];
                }
                else if (match[0]) {
                    const timeRef = match[0].toLowerCase();
                    const now = new Date();
                    if (timeRef.includes('today')) {
                        dueDate = now.toISOString().split('T')[0];
                    }
                    else if (timeRef.includes('tomorrow')) {
                        const tomorrow = new Date(now);
                        tomorrow.setDate(tomorrow.getDate() + 1);
                        dueDate = tomorrow.toISOString().split('T')[0];
                    }
                    else if (timeRef.includes('this week')) {
                        const endOfWeek = new Date(now);
                        endOfWeek.setDate(endOfWeek.getDate() + (7 - endOfWeek.getDay()));
                        dueDate = endOfWeek.toISOString().split('T')[0];
                    }
                    else if (timeRef.includes('next week')) {
                        const nextWeek = new Date(now);
                        nextWeek.setDate(nextWeek.getDate() + 7);
                        dueDate = nextWeek.toISOString().split('T')[0];
                    }
                }
                break;
            }
        }
        return { type, priority, dueDate, title };
    }
    inferTaskPriority(text, record) {
        const lowerText = text.toLowerCase();
        const revenue = record.AnnualRevenue || record.annual_revenue || 0;
        const status = (record.Status || record.status || '').toLowerCase();
        // High priority indicators
        if (lowerText.includes('urgent') || lowerText.includes('asap') || lowerText.includes('immediate')) {
            return 'high';
        }
        if (revenue > 1000000 || status.includes('hot') || status.includes('qualified')) {
            return 'high';
        }
        // Low priority indicators
        if (lowerText.includes('when possible') || lowerText.includes('low priority') || status.includes('cold')) {
            return 'low';
        }
        return 'medium';
    }
    matchesTimeFrame(task, timeFrame) {
        if (timeFrame === 'all')
            return true;
        const now = new Date();
        const taskDate = task.dueDate ? new Date(task.dueDate) : new Date(task.createdDate);
        switch (timeFrame) {
            case 'today':
                return taskDate.toDateString() === now.toDateString();
            case 'tomorrow':
                const tomorrow = new Date(now);
                tomorrow.setDate(tomorrow.getDate() + 1);
                return taskDate.toDateString() === tomorrow.toDateString();
            case 'this_week':
                const startOfWeek = new Date(now);
                startOfWeek.setDate(now.getDate() - now.getDay());
                const endOfWeek = new Date(startOfWeek);
                endOfWeek.setDate(startOfWeek.getDate() + 6);
                return taskDate >= startOfWeek && taskDate <= endOfWeek;
            case 'next_week':
                const nextWeekStart = new Date(now);
                nextWeekStart.setDate(now.getDate() + (7 - now.getDay()));
                const nextWeekEnd = new Date(nextWeekStart);
                nextWeekEnd.setDate(nextWeekStart.getDate() + 6);
                return taskDate >= nextWeekStart && taskDate <= nextWeekEnd;
            case 'this_month':
                return taskDate.getMonth() === now.getMonth() && taskDate.getFullYear() === now.getFullYear();
            default:
                return true;
        }
    }
    calculateTaskActionScore(task) {
        let score = 0;
        // Priority scoring
        switch (task.priority) {
            case 'high':
                score += 50;
                break;
            case 'medium':
                score += 30;
                break;
            case 'low':
                score += 10;
                break;
        }
        // Task type scoring
        switch (task.type) {
            case 'meetings':
                score += 40;
                break;
            case 'calls':
                score += 35;
                break;
            case 'demos':
                score += 35;
                break;
            case 'proposals':
                score += 30;
                break;
            case 'emails':
                score += 25;
                break;
            case 'follow_ups':
                score += 20;
                break;
            case 'research':
                score += 15;
                break;
        }
        // Revenue potential scoring
        const revenue = task.leadInfo?.revenue || 0;
        if (revenue > 1000000)
            score += 20;
        else if (revenue > 500000)
            score += 15;
        else if (revenue > 100000)
            score += 10;
        else if (revenue > 50000)
            score += 5;
        // Lead status scoring
        const status = (task.leadInfo?.status || '').toLowerCase();
        if (status.includes('hot') || status.includes('qualified'))
            score += 15;
        else if (status.includes('warm') || status.includes('interested'))
            score += 10;
        else if (status.includes('cold') || status.includes('unqualified'))
            score -= 5;
        // Due date urgency scoring
        if (task.dueDate) {
            const dueDate = new Date(task.dueDate);
            const now = new Date();
            const daysUntilDue = Math.ceil((dueDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
            if (daysUntilDue <= 0)
                score += 25; // Overdue
            else if (daysUntilDue <= 1)
                score += 20; // Due today/tomorrow
            else if (daysUntilDue <= 3)
                score += 15; // Due this week
            else if (daysUntilDue <= 7)
                score += 10; // Due next week
        }
        return Math.max(0, score);
    }
    calculateTaskPriorityScore(task) {
        switch (task.priority) {
            case 'high': return 100;
            case 'medium': return 50;
            case 'low': return 25;
            default: return 30;
        }
    }
    filterTaskByPriority(task, priorityLevel) {
        if (priorityLevel === 'all')
            return true;
        return task.priority === priorityLevel;
    }
    getTaskTypeBreakdown(tasks) {
        const breakdown = {};
        tasks.forEach(task => {
            breakdown[task.type] = (breakdown[task.type] || 0) + 1;
        });
        return breakdown;
    }
    generateTaskInsights(tasks) {
        const insights = [];
        if (tasks.length === 0) {
            insights.push("No priority tasks found matching your criteria.");
            return insights;
        }
        const highPriorityCount = tasks.filter(t => t.priority === 'high').length;
        const overdueCount = tasks.filter(t => {
            if (!t.dueDate)
                return false;
            return new Date(t.dueDate) < new Date();
        }).length;
        if (highPriorityCount > 0) {
            insights.push(`You have ${highPriorityCount} high-priority tasks requiring immediate attention.`);
        }
        if (overdueCount > 0) {
            insights.push(`${overdueCount} tasks are overdue and need urgent action.`);
        }
        const taskTypes = [...new Set(tasks.map(t => t.type))];
        if (taskTypes.length > 1) {
            insights.push(`Your tasks span ${taskTypes.length} different activity types: ${taskTypes.join(', ')}.`);
        }
        const topTask = tasks[0];
        if (topTask) {
            insights.push(`Your highest priority task is: "${topTask.title}" for ${topTask.contact.company}.`);
        }
        return insights;
    }
    // Helper methods for meeting preparation
    buildMeetingPrepPrompt(data) {
        return `Generate comprehensive meeting preparation notes for a ${data.meetingContext} with ${data.personName}${data.companyName ? ` from ${data.companyName}` : ''}.

CRM Data Available: ${data.crmData ? 'Yes' : 'No'}
Web Research Data: ${data.webResearchData ? 'Yes' : 'No'}
Focus Areas: ${data.focusAreas.join(', ') || 'General business discussion'}

Please provide:
1. Key talking points and conversation starters
2. Potential challenges and pain points to address
3. Strategic opportunities to explore
4. Recommended meeting outcomes and next steps

Make it actionable and specific to this person and company.`;
    }
    generateExecutiveSummary(crmData, webData, personName, companyName) {
        const leadData = this.extractLeadData(crmData);
        const hasData = crmData?.total_found > 0 || webData?.length > 0 || Object.keys(leadData).length > 0;
        let summary = '';
        // Person and company information
        const title = leadData.title || 'Unknown role';
        const actualCompany = leadData.company || companyName;
        const industry = leadData.industry;
        // Enhanced summary with qualification status
        if (leadData.qualificationStatus === 'Sales Qualified' || leadData.qualificationStatus === 'Highly Qualified') {
            summary += `šŸ”„ HIGH PRIORITY: Meeting with ${personName}, ${title} at ${actualCompany}. `;
            summary += `This is a SALES QUALIFIED prospect with strong potential. `;
        }
        else if (leadData.qualificationStatus === 'Marketing Qualified') {
            summary += `šŸ“ˆ Meeting with ${personName}, ${title} at ${actualCompany}. `;
            summary += `Marketing qualified prospect - focus on needs qualification and solution fit. `;
        }
        else {
            summary += `Meeting with ${personName}, ${title}${actualCompany ? ` at ${actualCompany}` : ''}. `;
        }
        // Industry context
        if (industry) {
            summary += `Operating in ${industry} sector. `;
        }
        // Company size and revenue context
        if (leadData.numberOfEmployees || leadData.annualRevenue) {
            const sizeCategory = this.categorizeCompanySize(leadData.numberOfEmployees, leadData.annualRevenue);
            summary += `${sizeCategory} organization. `;
        }
        // Qualification scores if available
        if (leadData.qualificationScore) {
            summary += `Qualification score: ${leadData.qualificationScore}/100. `;
        }
        // Key pain points from description
        if (leadData.description && leadData.description.length > 20) {
            const painPoints = this.extractPainPointsFromText(leadData.description);
            if (painPoints.length > 0) {
                summary += `Key documented needs: ${painPoints.slice(0, 2).join(', ')}. `;
            }
        }
        // Next step information
        if (leadData.nextStep) {
            summary += `Documented next step: ${leadData.nextStep}. `;
        }
        // Research context
        if (webData?.length > 0) {
            summary += `Recent web research completed across ${webData.length} sources providing market context. `;
        }
        // Recommendation based on qualification
        if (leadData.qualificationStatus === 'Sales Qualified') {
            summary += `RECOMMENDATION: Focus on solution demonstration, ROI discussion, and closing next steps.`;
        }
        else if (leadData.qualificationStatus === 'Marketing Qualified') {
            summary += `RECOMMENDATION: Complete BANT qualification and identify specific use cases.`;
        }
        else if (!hasData) {
            summary += `Limited data available - recommend focusing on discovery questions and relationship building.`;
        }
        else {
            summary += `RECOMMENDATION: Leverage documented challenges to demonstrate relevant solutions and build credibility.`;
        }
        return summary;
    }
    extractPersonProfile(crmData, webData, personName) {
        const leadData = this.extractLeadData(crmData);
        // Enhanced person profile with comprehensive data
        const profile = {
            name: personName,
            title: leadData.title || 'Unknown',
            company: leadData.company || 'Unknown',
            email: leadData.email || null,
            phone: leadData.phone || null,
            linkedin_url: leadData.linkedin_url || null,
            industry: leadData.industry || null,
            website: leadData.website || null,
            address: leadData.address || null,
            crm_provider: leadData.crmProvider || null,
            crm_id: leadData.crmId || null,
            source: leadData.source || null,
            record_type: leadData.recordType || 'Unknown',
            web_insights: webData?.length ? `Research completed from ${webData.length} sources` : null,
            // Enhanced qualification information
            qualification_status: leadData.qualificationStatus,
            qualification_score: leadData.qualificationScore,
            bant_scores: {
                budget: leadData.bantBudgetScore,
                authority: leadData.bantAuthorityScore,
                need: leadData.bantNeedScore,
                timeline: leadData.bantTimelineScore
            },
            // Lead-specific information
            lead_status: leadData.status,
            lead_rating: leadData.rating,
            annual_revenue: leadData.annualRevenue,
            employee_count: leadData.numberOfEmployees,
            description: leadData.description,
            next_step: leadData.nextStep,
            // Engagement insights
            last_activity: leadData.lastActivity,
            created_date: leadData.createdDate,
            modified_date: leadData.modifiedDate
        };
        // Add web research insights
        if (webData?.length > 0) {
            profile.web_research_summary = this.summarizeWebResearch(webData);
        }
        return profile;
    }
    extractCompanyOverview(crmData, webData, companyName) {
        const leadData = this.extractLeadData(crmData);
        const overview = {
            name: companyName,
            industry: leadData.industry || 'Unknown Industry',
            annual_revenue: leadData.annualRevenue || 'Unknown',
            employee_count: leadData.numberOfEmployees || 'Unknown',
            website: leadData.website,
            location: this.formatAddress(leadData),
            // Enhanced company intelligence
            business_model: this.inferBusinessModel(leadData),
            company_size_category: this.categorizeCompanySize(leadData.numberOfEmployees, leadData.annualRevenue),
            // Research-based insights
            recent_developments: webData?.length ? this.extractDevelopments(webData) : 'No recent data available',
            market_position: this.assessMarketPosition(leadData, webData),
            technology_profile: this.assessTechnologyProfile(leadData, webData),
            // Pain points and opportunities specific to this company
            potential_challenges: this.identifyCompanySpecificChallenges(leadData, webData),
            growth_opportunities: this.identifyGrowthOpportunities(leadData, webData),
            competitive_landscape: webData?.length ? 'Analysis based on recent research' : 'To be researched',
            // Decision-making insights
            buying_signals: this.identifyBuyingSignals(leadData),
            decision_timeline: this.estimateDecisionTimeline(leadData),
            budget_indicators: this.assessBudgetIndicators(leadData)
        };
        // Add industry-specific insights
        if (leadData.industry) {
            overview.industry_trends = this.getIndustryTrends(leadData.industry);
            overview.industry_specific_pain_points = this.getIndustrySpecificPainPoints(leadData.industry);
        }
        return overview;
    }
    generateConversationStarters(crmData, webData, context) {
        const starters = [];
        // Get lead/contact data
        const leadData = this.extractLeadData(crmData);
        const personName = leadData.name || 'Unknown';
        const companyName = leadData.company;
        const title = leadData.title;
        const industry = leadData.industry;
        const description = leadData.description;
        const qualificationStatus = leadData.qualificationStatus;
        // Personalized opening based on lead information
        if (leadData.name && leadData.title && leadData.company) {
            starters.push(`Hi ${personName}, thank you for taking the time to meet with me today. I understand you're the ${title} at ${companyName} - I'm excited to learn more about your current initiatives.`);
        }
        else {
            starters.push(`Thank you for taking the time to meet with me about ${context}`);
        }
        // Industry-specific conversation starters
        if (industry) {
            starters.push(`I've been working with several companies in the ${industry} space recently - I'd love to hear your perspective on the current market challenges.`);
        }
        // Lead-specific challenges from description
        if (description && description.length > 20) {
            const challengeKeywords = ['challenge', 'problem', 'issue', 'struggle', 'difficulty', 'pain', 'bottleneck'];
            const hasChallenge = challengeKeywords.some(keyword => description.toLowerCase().includes(keyword));
            if (hasChallenge) {
                starters.push(`I noticed in our previous discussions some of the challenges you mentioned - I'd like to dive deeper into those areas and see how we might help.`);
            }
        }
        // Qualification-based starters
        if (qualificationStatus === 'Sales Qualified' || qualificationStatus === 'Highly Qualified') {
            starters.push(`Based on our previous conversations, it sounds like you're actively looking for solutions in this area - I'd love to understand your evaluation process and timeline.`);
        }
        // Web research insights
        if (webData?.length > 0) {
            if (companyName) {
                starters.push(`I did some research on ${companyName} and saw some interesting developments - I'd love to get your take on how the market dynamics are affecting your business.`);
            }
            else {
                starters.push('I did some research on your industry and would love to get your perspective on the current trends.');
            }
        }
        // Fallback if no data available
        if (starters.length === 0) {
            starters.push(`Thank you for taking the time to meet with me about ${context}`);
            starters.push('I\'d love to learn more about your current challenges and priorities');
        }
        // Ensure we have at least 2 starters but no more than 4
        return starters.slice(0, Math.max(2, Math.min(4, starters.length)));
    }
    identifyPainPoints(crmData, webData, companyName) {
        const painPoints = [];
        const leadData = this.extractLeadData(crmData);
        // Extract pain points from lead description
        const description = leadData.description || '';
        if (description.length > 20) {
            const extractedPains = this.extractPainPointsFromText(description);
            painPoints.push(...extractedPains);
        }
        // Add qualification-based pain points
        if (leadData.qualificationAnalysis) {
            const analysisText = leadData.qualificationAnalysis.toString();
            const analysisPains = this.extractPainPointsFromText(analysisText);
            painPoints.push(...analysisPains);
        }
        // Industry-specific pain points based on actual industry data
        if (leadData.industry) {
            const industryPains = this.getIndustrySpecificPainPoints(leadData.industry);
            painPoints.push(...industryPains);
        }
        // Company size-based challenges
        if (leadData.numberOfEmployees || leadData.annualRevenue) {
            const sizePains = this.getCompanySizeBasedPainPoints(leadData.numberOfEmployees, leadData.annualRevenue);
            painPoints.push(...sizePains);
        }
        // Web research insights about company challenges
        if (webData?.length > 0) {
            webData.forEach((research) => {
                if (research.insights) {
                    const webPains = this.extractPainPointsFromText(research.insights);
                    painPoints.push(...webPains);
                }
            });
        }
        // Remove duplicates and ensure uniqueness
        const uniquePainPoints = [...new Set(painPoints)].filter(point => point.length > 10);
        // Add fallback generic points if no specific ones found
        if (uniquePainPoints.length < 2) {
            const genericPains = [
                `Operational efficiency challenges${companyName ? ` at ${companyName}` : ''}`,
                'Digital transformation and technology integration needs',
                'Process optimization and cost reduction opportunities'
            ];
            uniquePainPoints.push(...genericPains.filter(p => !uniquePainPoints.includes(p)));
        }
        return uniquePainPoints.slice(0, 6); // Maximum 6 pain points
    }
    suggestMeetingObjectives(context, crmData) {
        const objectives = [];
        const leadData = this.extractLeadData(crmData);
        // Lead-specific objectives based on qualification status
        if (leadData.qualificationStatus === 'Sales Qualified' || leadData.qualificationStatus === 'Highly Qualified') {
            objectives.push('Present tailored solution recommendations based on identified needs');
            objectives.push('Discuss implementation timeline and next steps');
            objectives.push('Address any remaining concerns or objections');
        }
        else if (leadData.qualificationStatus === 'Marketing Qualified') {
            objectives.push('Qualify budget, authority, need, and timeline (BANT)');
            objectives.push('Understand decision-making process and key stakeholders');
            objectives.push('Build credibility and demonstrate expertise');
        }
        else {
            objectives.push('Discover specific business challenges and pain points');
            objectives.push('Understand current processes and technology stack');
            objectives.push('Assess fit and qualification criteria');
        }
        // Context-specific objectives
        if (context.includes('demo')) {
            if (leadData.industry) {
                objectives.push(`Demonstrate ${leadData.industry}-specific use cases and ROI`);
            }
            else {
                objectives.push('Demonstrate relevant product capabilities');
            }
            objectives.push('Gather feedback on proposed solution approach');
        }
        if (context.includes('follow-up')) {
            objectives.push('Address questions and concerns from previous interactions');
            if (leadData.description?.includes('proposal') || leadData.description?.includes('quote')) {
                objectives.push('Review and finalize proposal details');
            }
        }
        if (context.includes('discovery')) {
            objectives.push('Map current technology landscape and integration requirements');
            objectives.push('Identify key success metrics and ROI expectations');
        }
        // NextStep-based objectives from CRM
        if (leadData.nextStep) {
            const nextStepObjective = `Execute planned next step: ${leadData.nextStep}`;
            if (!objectives.some(obj => obj.includes(leadData.nextStep))) {
                objectives.push(nextStepObjective);
            }
        }
        // Ensure we have core objectives if nothing specific
        if (objectives.length < 3) {
            const coreObjectives = [
                'Build rapport and establish trust',
                'Understand current business challenges and priorities',
                'Determine next steps and timeline'
            ];
            objectives.push(...coreObjectives.filter(obj => !objectives.includes(obj)));
        }
        return objectives.slice(0, 6); // Maximum 6 objectives
    }
    generateActionItems(crmData, context) {
        const actions = [];
        const leadData = this.extractLeadData(crmData);
        // Standard post-meeting actions
        actions.push('Send personalized follow-up email with meeting summary and key takeaways');
        actions.push('Update CRM with detailed meeting notes, outcomes, and next steps');
        // Qualification-specific actions
        if (leadData.qualificationStatus === 'Sales Qualified' || leadData.qualificationStatus === 'Highly Qualified') {
            actions.push('Prepare detailed proposal with ROI analysis and implementation timeline');
            actions.push('Schedule technical discovery call with solution architects');
            actions.push('Provide relevant case studies from similar companies in their industry');
        }
        else if (leadData.qualificationStatus === 'Marketing Qualified') {
            actions.push('Send educational content about our solutions and success stories');
            actions.push('Schedule follow-up call to continue qualification process');
        }
        else {
            actions.push('Research their company and industry challenges further');
            actions.push('Prepare nurturing sequence with valuable industry insights');
        }
        // Industry-specific actions
        if (leadData.industry) {
            actions.push(`Compile industry-specific resources and insights for ${leadData.industry} sector`);
        }
        // Context-specific actions
        if (context.includes('demo')) {
            actions.push('Follow up on demo feedback and address any technical questions');
            actions.push('Provide demo recording and additional technical documentation');
        }
        if (context.includes('proposal')) {
            actions.push('Refine proposal based on meeting feedback and requirements');
            actions.push('Schedule proposal review meeting with decision makers');
        }
        if (context.includes('discovery')) {
            actions.push('Create detailed requirements document based on discovery findings');
            actions.push('Prepare customized solution architecture and integration plan');
        }
        // Lead-specific actions from description or next steps
        if (leadData.nextStep && !actions.some(action => action.includes(leadData.nextStep))) {
            actions.push(`Execute documented next step: ${leadData.nextStep}`);
        }
        if (leadData.description) {
            const urgentKeywords = ['urgent', 'asap', 'immediate', 'quickly', 'rush'];
            const isUrgent = urgentKeywords.some(keyword => leadData.description.toLowerCase().includes(keyword));
            if (isUrgent) {
                actions.push('Prioritize rapid response due to urgency indicators');
            }
        }
        // Stakeholder engagement actions
        if (leadData.title?.toLowerCase().includes('manager') || leadData.title?.toLowerCase().includes('director')) {
            actions.push('Identify and engage with additional decision makers and influencers');
        }
        return actions.slice(0, 8); // Maximum 8 action items
    }
    // Advanced AI synthesis helper methods
    formatMeetingPrepInsights(analysis, personName, meetingContext) {
        try {
            // Extract key insights from ROSA's analysis
            let insights = `AI-Powered Meeting Preparation for ${personName}:\n\n`;
            if (analysis.qualification_score) {
                insights += `šŸŽÆ Lead Qualification Score: ${analysis.qualification_score}/100\n`;
            }
            if (analysis.pain_points && Array.isArray(analysis.pain_points)) {
                insights += `\nāš ļø Identified Pain Points:\n${analysis.pain_points.map((point) => `• ${point}`).join('\n')}\n`;
            }
            if (analysis.recommendations && Array.isArray(analysis.recommendations)) {
                insights += `\nšŸ’” AI Recommendations:\n${analysis.recommendations.map((rec) => `• ${rec}`).join('\n')}\n`;
            }
            if (analysis.next_best_actions && Array.isArray(analysis.next_best_actions)) {
                insights += `\nšŸŽÆ Next Best Actions:\n${analysis.next_best_actions.map((action) => `• ${action}`).join('\n')}\n`;
            }
            if (analysis.talking_points && Array.isArray(analysis.talking_points)) {
                insights += `\nšŸ—£ļø AI-Generated Talking Points:\n${analysis.talking_points.map((point) => `• ${point}`).join('\n')}\n`;
            }
            // Add context-specific insights
            insights += `\nšŸ¤– Meeting Context: Optimized for ${meetingContext} with personalized approach based on CRM and web research data.`;
            return insights;
        }
        catch (error) {
            return `AI Analysis completed for ${personName}. Raw data: ${JSON.stringify(analysis).substring(0, 300)}...`;
        }
    }
    generateEnhancedMeetingInsights(crmData, webResearchData, personName, companyName, meetingContext, focusAreas) {
        let insights = `Enhanced Meeting Preparation Insights for ${personName}:\n\n`;
        // CRM-based insights
        if (crmData && crmData.total_found > 0) {
            const crmRecord = crmData.contacts?.[0] || crmData.leads?.[0];
            const actualCompany = crmRecord?.company || crmRecord?.Company || companyName;
            const title = crmRecord?.title || crmRecord?.Title;
            const status = crmRecord?.status || crmRecord?.Status;
            insights += `šŸ¢ CRM Profile: ${title} at ${actualCompany}\n`;
            insights += `šŸ“Š Status: ${status} (${crmData.contacts?.length > 0 ? 'Contact' : 'Lead'})\n`;
            if (crmRecord?.email) {
                insights += `šŸ“§ Contact: ${crmRecord.email}\n`;
            }
            // Role-specific insights
            if (title) {
                if (title.toLowerCase().includes('vp') || title.toLowerCase().includes('vice president')) {
                    insights += `\nšŸ’¼ VP-Level Approach: Focus on strategic initiatives, ROI, and organizational impact.\n`;
                }
                else if (title.toLowerCase().includes('director')) {
                    insights += `\nšŸ’¼ Director-Level Approach: Emphasize operational efficiency and team productivity.\n`;
                }
                else if (title.toLowerCase().includes('manager')) {
                    insights += `\nšŸ’¼ Manager-Level Approach: Discuss day-to-day challenges and process improvements.\n`;
                }
            }
        }
        // Web research insights
        if (webResearchData && webResearchData.length > 0) {
            insights += `\n🌐 Research Completed: ${webResearchData.length} web sources analyzed for current market context.\n`;
        }
        // Focus area insights
        if (focusAreas && focusAreas.length > 0) {
            insights += `\nšŸŽÆ Key Focus Areas to Discuss:\n${focusAreas.map(area => `• ${area}`).join('\n')}\n`;
        }
        // Meeting context insights
        if (meetingContext) {
            insights += `\nšŸ“ž Meeting Type: ${meetingContext}\n`;
            if (meetingContext.includes('demo')) {
                insights += `• Prepare interactive demonstration tailored to their use case\n• Focus on specific features that address their pain points\n`;
            }
            else if (meetingContext.includes('sales')) {
                insights += `• Emphasize value proposition and ROI\n• Prepare pricing discussion and next steps\n`;
            }
            else if (meetingContext.includes('discovery')) {
                insights += `• Ask open-ended questions about current processes\n• Identify decision-making criteria and timeline\n`;
            }
        }
        insights += `\nšŸ¤– AI Recommendation: This enhanced analysis combines CRM data, web research, and contextual intelligence to optimize your meeting approach.`;
        return insights;
    }
    // Email Integration Handler Methods
    async handleEmailAutoRespond(request) {
        try {
            const result = await this.emailHandlers.handleEmailAutoRespond(request.params.arguments);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Email auto-respond handler error:', error);
            throw new McpError(ErrorCode.InternalError, `Email auto-respond failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleEmailIntentAnalysis(request) {
        try {
            const result = await this.emailHandlers.handleEmailIntentAnalysis(request.params.arguments);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Email intent analysis handler error:', error);
            throw new McpError(ErrorCode.InternalError, `Email intent analysis failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleCompileSalesInfo(request) {
        try {
            const result = await this.emailHandlers.handleCompileSalesInfo(request.params.arguments);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Compile sales info handler error:', error);
            throw new McpError(ErrorCode.InternalError, `Compile sales info failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleGenerateEmailResponse(request) {
        try {
            const result = await this.emailHandlers.handleGenerateEmailResponse(request.params.arguments);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Generate email response handler error:', error);
            throw new McpError(ErrorCode.InternalError, `Generate email response failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleScheduleEmailFollowup(request) {
        try {
            const result = await this.emailHandlers.handleScheduleEmailFollowup(request.params.arguments);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Schedule email followup handler error:', error);
            throw new McpError(ErrorCode.InternalError, `Schedule email followup failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleEmailAnalytics(request) {
        try {
            const result = await this.emailHandlers.handleEmailAnalytics(request.params.arguments);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Email analytics handler error:', error);
            throw new McpError(ErrorCode.InternalError, `Email analytics failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    // New Email Detection & Sending Tool Handlers
    async handleDetectEmails(request) {
        try {
            const result = await this.emailHandlers.handleEmailDetection(request.params.arguments);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Email detection handler error:', error);
            throw new McpError(ErrorCode.InternalError, `Email detection failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleSendEmail(request) {
        try {
            const result = await this.emailHandlers.handleEmailSending(request.params.arguments);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Email sending handler error:', error);
            throw new McpError(ErrorCode.InternalError, `Email sending failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleGetEmailStatus(request) {
        try {
            const result = await this.emailHandlers.handleEmailStatus(request.params.arguments);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Email status handler error:', error);
            throw new McpError(ErrorCode.InternalError, `Email status check failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleManageEmailTemplates(request) {
        try {
            const result = await this.emailHandlers.handleEmailTemplates(request.params.arguments);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Email template management handler error:', error);
            throw new McpError(ErrorCode.InternalError, `Email template management failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    async handleManageEmailCampaigns(request) {
        try {
            const result = await this.emailHandlers.handleEmailCampaigns(request.params.arguments);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(result, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            this.errorLog('Email campaign management handler error:', error);
            throw new McpError(ErrorCode.InternalError, `Email campaign management failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    // Helper methods for enhanced meeting preparation
    extractLeadData(crmData) {
        // Extract lead data from various CRM structures
        if (!crmData)
            return {};
        // Handle different CRM data structures
        let leadRecord = null;
        if (crmData.leads && Array.isArray(crmData.leads) && crmData.leads.length > 0) {
            leadRecord = crmData.leads[0];
        }
        else if (crmData.contacts && Array.isArray(crmData.contacts) && crmData.contacts.length > 0) {
            leadRecord = crmData.contacts[0];
        }
        else if (crmData.contact_info) {
            leadRecord = crmData.contact_info;
        }
        else if (typeof crmData === 'object') {
            leadRecord = crmData;
        }
        if (!leadRecord)
            return {};
        return {
            name: leadRecord.name || leadRecord.Name || `${leadRecord.FirstName || ''} ${leadRecord.LastName || ''}`.trim(),
            title: leadRecord.title || leadRecord.Title,
            company: leadRecord.company || leadRecord.Company,
            email: leadRecord.email || leadRecord.Email,
            phone: leadRecord.phone || leadRecord.Phone,
            linkedin_url: leadRecord.linkedin_url || leadRecord.LinkedInUrl,
            industry: leadRecord.industry || leadRecord.Industry,
            website: leadRecord.website || leadRecord.Website,
            address: leadRecord.address || leadRecord.Street,
            city: leadRecord.city || leadRecord.City,
            state: leadRecord.state || leadRecord.State,
            country: leadRecord.country || leadRecord.Country,
            crmProvider: leadRecord.crmProvider || leadRecord.CrmProvider,
            crmId: leadRecord.crmId || leadRecord.CrmId || leadRecord.Id,
            source: leadRecord.source || leadRecord.LeadSource,
            status: leadRecord.status || leadRecord.Status,
            rating: leadRecord.rating || leadRecord.Rating,
            description: leadRecord.description || leadRecord.Description,
            nextStep: leadRecord.nextStep || leadRecord.NextStep,
            qualificationStatus: leadRecord.qualificationStatus || leadRecord.QualificationStatus,
            qualificationScore: leadRecord.qualificationScore || leadRecord.QualificationScore,
            qualificationAnalysis: leadRecord.qualificationAnalysis || leadRecord.QualificationAnalysis,
            qualificationRecommendations: leadRecord.qualificationRecommendations || leadRecord.QualificationRecommendations,
            bantBudgetScore: leadRecord.bantBudgetScore || leadRecord.BantBudgetScore,
            bantAuthorityScore: leadRecord.bantAuthorityScore || leadRecord.BantAuthorityScore,
            bantNeedScore: leadRecord.bantNeedScore || leadRecord.BantNeedScore,
            bantTimelineScore: leadRecord.bantTimelineScore || leadRecord.BantTimelineScore,
            annualRevenue: leadRecord.annualRevenue || leadRecord.AnnualRevenue,
            numberOfEmployees: leadRecord.numberOfEmployees || leadRecord.NumberOfEmployees,
            createdDate: leadRecord.createdDate || leadRecord.CreatedDate,
            modifiedDate: leadRecord.modifiedDate || leadRecord.LastModifiedDate,
            lastActivity: leadRecord.lastActivity || leadRecord.LastActivity,
            recordType: this.determineRecordType(crmData)
        };
    }
    determineRecordType(crmData) {
        if (crmData.leads && crmData.leads.length > 0)
            return 'Lead';
        if (crmData.contacts && crmData.contacts.length > 0)
            return 'Contact';
        return 'Unknown';
    }
    extractPainPointsFromText(text) {
        if (!text || text.length < 10)
            return [];
        const painPoints = [];
        const lowerText = text.toLowerCase();
        // Keywords that indicate pain points
        const painKeywords = [
            'challenge', 'problem', 'issue', 'struggle', 'difficulty', 'pain', 'bottleneck',
            'inefficient', 'slow', 'manual', 'outdated', 'lacking', 'need', 'require',
            'improve', 'optimize', 'automate', 'streamline', 'eliminate', 'reduce'
        ];
        // Common business pain point patterns
        const painPatterns = [
            /(?:manual|time-consuming|inefficient)\s+(?:process|workflow|system)/gi,
            /(?:lack|need|require)\s+(?:automation|integration|visibility)/gi,
            /(?:struggling|difficulty)\s+with\s+(\w+(?:\s+\w+)*)/gi,
            /(?:challenge|problem|issue)\s+(?:with|around|regarding)\s+(\w+(?:\s+\w+)*)/gi,
            /(?:improve|optimize|streamline)\s+(\w+(?:\s+\w+)*)/gi
        ];
        // Extract sentences containing pain indicators
        const sentences = text.split(/[.!?]+/).map(s => s.trim()).filter(s => s.length > 10);
        sentences.forEach(sentence => {
            const lowerSentence = sentence.toLowerCase();
            // Check if sentence contains pain keywords
            const containsPainKeyword = painKeywords.some(keyword => lowerSentence.includes(keyword));
            if (containsPainKeyword) {
                // Extract meaningful pain points from the sentence
                painPatterns.forEach(pattern => {
                    const matches = sentence.match(pattern);
                    if (matches) {
                        matches.forEach(match => {
                            if (match.length > 15 && match.length < 100) {
                                painPoints.push(this.capitalizeFirstLetter(match.trim()));
                            }
                        });
                    }
                });
                // If no pattern matches, use the whole sentence if it's not too long
                if (sentence.length > 20 && sentence.length < 150 && containsPainKeyword) {
                    painPoints.push(this.capitalizeFirstLetter(sentence));
                }
            }
        });
        return [...new Set(painPoints)]; // Remove duplicates
    }
    getIndustrySpecificPainPoints(industry) {
        const industryPains = {
            'technology': [
                'Rapid technology obsolescence and need for continuous upgrades',
                'Scaling infrastructure to handle growing user base',
                'Data security and privacy compliance challenges'
            ],
            'healthcare': [
                'Regulatory compliance and documentation requirements',
                'Patient data management and interoperability issues',
                'Cost containment while maintaining quality of care'
            ],
            'financial services': [
                'Regulatory compliance and reporting complexity',
                'Legacy system modernization challenges',
                'Cybersecurity and fraud prevention concerns'
            ],
            'manufacturing': [
                'Supply chain disruptions and inventory management',
                'Equipment maintenance and downtime reduction',
                'Quality control and regulatory compliance'
            ],
            'retail': [
                'Omnichannel customer experience challenges',
                'Inventory management across multiple channels',
                'Customer data integration and personalization'
            ],
            'education': [
                'Student engagement and learning outcome measurement',
                'Technology integration in curriculum delivery',
                'Administrative efficiency and resource optimization'
            ]
        };
        const normalizedIndustry = industry.toLowerCase();
        // Try exact match first
        if (industryPains[normalizedIndustry]) {
            return industryPains[normalizedIndustry];
        }
        // Try partial matches
        for (const [key, pains] of Object.entries(industryPains)) {
            if (normalizedIndustry.includes(key) || key.includes(normalizedIndustry)) {
                return pains;
            }
        }
        // Generic business pain points
        return [
            `${industry} industry digital transformation challenges`,
            'Process automation and operational efficiency needs',
            'Customer experience optimization requirements'
        ];
    }
    getCompanySizeBasedPainPoints(employees, revenue) {
        const empCount = parseInt(employees?.toString() || '0');
        const annualRev = parseInt(revenue?.toString() || '0');
        if (empCount < 50 || annualRev < 5000000) {
            // Small business pain points
            return [
                'Limited resources for technology implementation',
                'Need for scalable solutions that grow with the business',
                'Manual processes due to budget constraints'
            ];
        }
        else if (empCount < 500 || annualRev < 100000000) {
            // Mid-market pain points
            return [
                'Outgrowing current systems and processes',
                'Need for better integration between departments',
                'Scalability challenges as business grows'
            ];
        }
        else {
            // Enterprise pain points
            return [
                'Complex integration requirements across multiple systems',
                'Compliance and governance at scale',
                'Managing change across large, distributed teams'
            ];
        }
    }
    summarizeWebResearch(webData) {
        if (!webData || webData.length === 0)
            return 'No web research available';
        const summaryPoints = [];
        webData.forEach((research, index) => {
            if (research.insights) {
                summaryPoints.push(`Source ${index + 1}: ${research.insights.substring(0, 100)}...`);
            }
            else if (research.content) {
                summaryPoints.push(`Source ${index + 1}: ${research.content.substring(0, 100)}...`);
            }
        });
        return summaryPoints.join(' | ');
    }
    formatAddress(leadData) {
        const parts = [leadData.city, leadData.state, leadData.country].filter(Boolean);
        return parts.length > 0 ? parts.join(', ') : 'Unknown Location';
    }
    inferBusinessModel(leadData) {
        const revenue = parseInt(leadData.annualRevenue?.toString() || '0');
        const employees = parseInt(leadData.numberOfEmployees?.toString() || '0');
        const industry = leadData.industry?.toLowerCase() || '';
        if (industry.includes('saas') || industry.includes('software')) {
            return 'Software as a Service (SaaS)';
        }
        else if (industry.includes('retail') || industry.includes('e-commerce')) {
            return 'Retail/E-commerce';
        }
        else if (industry.includes('consulting') || industry.includes('services')) {
            return 'Professional Services';
        }
        else if (industry.includes('manufacturing')) {
            return 'Manufacturing/Production';
        }
        else if (revenue > 100000000) {
            return 'Large Enterprise';
        }
        else if (revenue > 10000000) {
            return 'Mid-Market';
        }
        else {
            return 'Small to Medium Business';
        }
    }
    categorizeCompanySize(employees, revenue) {
        const empCount = parseInt(employees?.toString() || '0');
        const annualRev = parseInt(revenue?.toString() || '0');
        if (empCount > 1000 || annualRev > 100000000) {
            return 'Enterprise (1000+ employees)';
        }
        else if (empCount > 250 || annualRev > 25000000) {
            return 'Large Company (250-1000 employees)';
        }
        else if (empCount > 50 || annualRev > 5000000) {
            return 'Mid-Market (50-250 employees)';
        }
        else {
            return 'Small Business (<50 employees)';
        }
    }
    extractDevelopments(webData) {
        const developments = [];
        webData.forEach((research) => {
            if (research.insights) {
                // Look for news, announcements, changes
                const newsKeywords = ['announced', 'launched', 'acquired', 'partnership', 'funding', 'expansion'];
                const insights = research.insights.toLowerCase();
                if (newsKeywords.some(keyword => insights.includes(keyword))) {
                    developments.push(research.insights.substring(0, 150));
                }
            }
        });
        return developments.length > 0 ? developments.join('. ') : 'No recent developments identified from research';
    }
    assessMarketPosition(leadData, webData) {
        const revenue = parseInt(leadData.annualRevenue?.toString() || '0');
        const employees = parseInt(leadData.numberOfEmployees?.toString() || '0');
        if (revenue > 1000000000) {
            return 'Market Leader - Large enterprise with significant market presence';
        }
        else if (revenue > 100000000) {
            return 'Established Player - Strong market position in their segment';
        }
        else if (revenue > 10000000) {
            return 'Growing Company - Expanding market presence';
        }
        else {
            return 'Emerging Player - Building market presence';
        }
    }
    assessTechnologyProfile(leadData, webData) {
        const industry = leadData.industry?.toLowerCase() || '';
        const employees = parseInt(leadData.numberOfEmployees?.toString() || '0');
        if (industry.includes('technology') || industry.includes('software')) {
            return 'Technology-forward organization with likely modern tech stack';
        }
        else if (employees > 500) {
            return 'Large organization likely using enterprise-grade technology solutions';
        }
        else if (employees > 50) {
            return 'Mid-size organization with mixed technology maturity';
        }
        else {
            return 'Small organization likely using basic technology solutions';
        }
    }
    identifyCompanySpecificChallenges(leadData, webData) {
        const challenges = [];
        // Based on company size
        const sizeCategory = this.categorizeCompanySize(leadData.numberOfEmployees, leadData.annualRevenue);
        if (sizeCategory.includes('Small')) {
            challenges.push('Resource constraints and need for cost-effective solutions');
        }
        else if (sizeCategory.includes('Enterprise')) {
            challenges.push('Complex integration requirements and enterprise-scale challenges');
        }
        // Based on industry
        if (leadData.industry) {
            const industryPains = this.getIndustrySpecificPainPoints(leadData.industry);
            challenges.push(...industryPains.slice(0, 2)); // Take first 2
        }
        // Based on description
        if (leadData.description) {
            const extractedChallenges = this.extractPainPointsFromText(leadData.description);
            challenges.push(...extractedChallenges.slice(0, 2)); // Take first 2
        }
        return challenges.slice(0, 4); // Maximum 4 challenges
    }
    identifyGrowthOpportunities(leadData, webData) {
        const opportunities = [];
        // Based on qualification status
        if (leadData.qualificationStatus === 'Sales Qualified') {
            opportunities.push('High potential for immediate solution implementation');
        }
        // Based on company growth indicators
        if (leadData.annualRevenue && parseInt(leadData.annualRevenue.toString()) > 50000000) {
            opportunities.push('Established company with budget for technology investments');
        }
        // Based on industry trends
        if (leadData.industry) {
            const industry = leadData.industry.toLowerCase();
            if (industry.includes('technology')) {
                opportunities.push('Technology sector growth and digital transformation initiatives');
            }
            else if (industry.includes('healthcare')) {
                opportunities.push('Healthcare digital transformation and regulatory modernization');
            }
            else {
                opportunities.push('Industry digitization and process optimization opportunities');
            }
        }
        return opportunities.slice(0, 3); // Maximum 3 opportunities
    }
    identifyBuyingSignals(leadData) {
        const signals = [];
        if (leadData.qualificationStatus === 'Sales Qualified') {
            signals.push('Qualified as sales-ready prospect');
        }
        if (leadData.description) {
            const urgentKeywords = ['urgent', 'asap', 'immediate', 'quickly', 'timeline', 'deadline'];
            if (urgentKeywords.some(keyword => leadData.description.toLowerCase().includes(keyword))) {
                signals.push('Urgency indicators in communication');
            }
            const budgetKeywords = ['budget', 'funding', 'approved', 'allocated', 'investment'];
            if (budgetKeywords.some(keyword => leadData.description.toLowerCase().includes(keyword))) {
                signals.push('Budget discussion indicators');
            }
        }
        if (leadData.nextStep && leadData.nextStep.toLowerCase().includes('demo')) {
            signals.push('Active engagement - demo scheduled');
        }
        return signals;
    }
    estimateDecisionTimeline(leadData) {
        if (leadData.qualificationStatus === 'Sales Qualified') {
            return '30-60 days - Active evaluation phase';
        }
        else if (leadData.qualificationStatus === 'Marketing Qualified') {
            return '60-90 days - Early consideration phase';
        }
        else if (leadData.description?.toLowerCase().includes('urgent')) {
            return '2-4 weeks - Urgent requirement';
        }
        else {
            return '90+ days - Long-term evaluation';
        }
    }
    assessBudgetIndicators(leadData) {
        const revenue = parseInt(leadData.annualRevenue?.toString() || '0');
        if (revenue > 100000000) {
            return 'High budget potential - Enterprise-scale revenue';
        }
        else if (revenue > 25000000) {
            return 'Moderate to high budget potential - Mid-market revenue';
        }
        else if (revenue > 5000000) {
            return 'Limited to moderate budget potential - SMB revenue';
        }
        else {
            return 'Budget constraints likely - Small business revenue';
        }
    }
    getIndustryTrends(industry) {
        const industryTrends = {
            'technology': [
                'AI and automation adoption acceleration',
                'Cloud-first infrastructure strategies',
                'Cybersecurity investment prioritization'
            ],
            'healthcare': [
                'Telemedicine and digital health expansion',
                'Electronic health records modernization',
                'Patient experience digitization'
            ],
            'financial services': [
                'Digital banking transformation',
                'RegTech adoption for compliance',
                'Open banking and API ecosystem development'
            ]
        };
        const normalizedIndustry = industry.toLowerCase();
        return industryTrends[normalizedIndustry] || [
            'Digital transformation initiatives',
            'Process automation adoption',
            'Customer experience enhancement focus'
        ];
    }
    capitalizeFirstLetter(text) {
        return text.charAt(0).toUpperCase() + text.slice(1);
    }
    // Rosa-style Action Score Calculation
    calculateRosaActionScore(lead) {
        let score = 0;
        // Base qualification score (0-40 points)
        const qualScore = parseFloat(lead.qualificationScore || lead.QualificationScore || 0);
        score += Math.min(qualScore * 0.4, 40); // Cap at 40 points
        // Revenue/Company size (0-25 points)
        const revenue = parseFloat(lead.AnnualRevenue || lead.annualRevenue || 0);
        if (revenue > 100000000)
            score += 25; // $100M+ = 25 pts
        else if (revenue > 50000000)
            score += 20; // $50M+ = 20 pts
        else if (revenue > 10000000)
            score += 15; // $10M+ = 15 pts
        else if (revenue > 1000000)
            score += 10; // $1M+ = 10 pts
        else if (revenue > 0)
            score += 5; // Any revenue = 5 pts
        // Lead status/rating (0-20 points)
        const status = (lead.Status || lead.status || '').toLowerCase();
        const rating = (lead.Rating || lead.rating || '').toLowerCase();
        if (status === 'qualified' || rating === 'hot')
            score += 20;
        else if (status === 'working' || rating === 'warm')
            score += 15;
        else if (status === 'new' || rating === 'medium')
            score += 10;
        else if (status === 'nurture' || rating === 'cold')
            score += 5;
        // Activity recency (0-10 points)
        const lastActivity = lead.LastActivity || lead.lastActivity;
        if (lastActivity) {
            const daysSinceActivity = Math.floor((Date.now() - new Date(lastActivity).getTime()) / (1000 * 60 * 60 * 24));
            if (daysSinceActivity <= 1)
                score += 10; // Today/yesterday = 10 pts
            else if (daysSinceActivity <= 7)
                score += 7; // This week = 7 pts
            else if (daysSinceActivity <= 30)
                score += 4; // This month = 4 pts
        }
        // Next step urgency (0-5 points)
        const nextStep = (lead.NextStep || lead.nextStep || '').toLowerCase();
        if (nextStep.includes('demo') || nextStep.includes('meeting') || nextStep.includes('call'))
            score += 5;
        else if (nextStep.includes('follow-up') || nextStep.includes('proposal'))
            score += 3;
        // Description engagement indicators (bonus points)
        const description = (lead.Description || lead.description || '').toLowerCase();
        if (description.includes('browsing') || description.includes('interested'))
            score += 2;
        if (description.includes('urgent') || description.includes('hot'))
            score += 3;
        return Math.min(Math.round(score), 100); // Cap at 100
    }
    // Rosa Category Determination
    determineRosaCategory(lead) {
        const status = (lead.Status || lead.status || '').toLowerCase();
        const rating = (lead.Rating || lead.rating || '').toLowerCase();
        const nextStep = (lead.NextStep || lead.nextStep || '').toLowerCase();
        const description = (lead.Description || lead.description || '').toLowerCase();
        // High engagement categories
        if (status === 'qualified' || rating === 'hot' || description.includes('hot')) {
            return 'Hot Lead, Qualified';
        }
        if (nextStep.includes('meeting') || nextStep.includes('demo')) {
            return 'Meeting, Existing Product Expansion';
        }
        if (status === 'new' || description.includes('new')) {
            return 'First Product, New Product Expansion';
        }
        // Lower engagement categories
        if (status === 'unqualified' || !lead.LastActivity) {
            return 'Not Contacted, Expiring Opportunities';
        }
        if (status === 'nurture') {
            return 'Not Contacted, Expiring Triggers';
        }
        // Default
        return 'Active Prospect, Standard Priority';
    }
    // Extract upcoming "meetings" from NextStep fields and descriptions
    extractUpcomingMeetings(leads) {
        const meetings = [];
        leads.forEach(lead => {
            const nextStep = (lead.NextStep || lead.nextStep || '').toString();
            const description = (lead.Description || lead.description || '').toString();
            const leadName = lead.FirstName ? `${lead.FirstName} ${lead.LastName || ''}`.trim() : lead.name;
            const company = lead.Company || lead.company;
            // Check NextStep field for meetings
            if (nextStep && nextStep.length > 0) {
                const meetingKeywords = ['meeting', 'call', 'demo', 'appointment', 'discussion', 'presentation'];
                const hasMeetingKeyword = meetingKeywords.some(keyword => nextStep.toLowerCase().includes(keyword));
                if (hasMeetingKeyword) {
                    meetings.push({
                        person: leadName,
                        company: company,
                        context: nextStep,
                        priority: this.extractPriorityFromNextStep(nextStep),
                        estimated_time: this.extractTimeFromNextStep(nextStep),
                        source: 'NextStep Field'
                    });
                }
            }
            // Check description field for meeting references
            if (description && description.length > 20) {
                const meetingPatterns = [
                    /meeting.*(?:scheduled|planned|set)/i,
                    /(?:call|demo).*(?:scheduled|planned|set)/i,
                    /(?:scheduled|planning).*(?:meeting|call|demo)/i
                ];
                meetingPatterns.forEach(pattern => {
                    const match = description.match(pattern);
                    if (match) {
                        meetings.push({
                            person: leadName,
                            company: company,
                            context: match[0],
                            priority: this.extractPriorityFromNextStep(match[0]),
                            estimated_time: this.extractTimeFromNextStep(match[0]),
                            source: 'Description Field'
                        });
                    }
                });
            }
        });
        // Remove duplicates and return
        const uniqueMeetings = meetings.filter((meeting, index, self) => index === self.findIndex(m => m.person === meeting.person && m.company === meeting.company));
        return uniqueMeetings;
    }
    // Fetch priority tasks from CRM Connect API
    async fetchPriorityTasksFromCRM() {
        try {
            this.debugLog('[MCP] Fetching priority tasks from CRM Connect API');
            // Try to query CRM Connect tasks API
            const tasksResult = await this.apiService.queryCRMTasks({
                limit: 50,
                offset: 0,
                sort_by: 'priority',
                sort_order: 'desc'
            }, this.userToken);
            if (!tasksResult.success) {
                this.debugLog(`[MCP] CRM tasks API failed: ${tasksResult.error}, using fallback approach`);
                // Fallback: Extract tasks from lead descriptions and next steps
                return this.extractTasksFromLeadData();
            }
            if (!Array.isArray(tasksResult.data) || tasksResult.data.length === 0) {
                this.debugLog('[MCP] No tasks found in CRM, checking lead data for task information');
                return this.extractTasksFromLeadData();
            }
            const tasks = tasksResult.data.map((task) => ({
                id: task.id,
                task: task.subject || task.Subject || 'Unnamed Task',
                description: task.description || task.Description || '',
                priority: this.normalizePriority(task.priority || task.Priority),
                status: task.status || task.Status || 'Unknown',
                due: this.formatTaskDueDate(task.dueDate || task.ActivityDate),
                created: task.createdDate || task.CreatedDate,
                lead_id: task.relatedToId || task.whoId,
                lead_name: task.relatedToName || 'Unknown Contact',
                company: task.relatedToCompany || 'Unknown Company',
                crm_provider: task.crmProvider || 'Unknown CRM',
                rosa_summary: `${this.normalizePriority(task.priority || task.Priority).toUpperCase()}: ${task.subject || 'Task'}`
            }));
            // Sort by Rosa priority scoring
            return tasks
                .sort((a, b) => this.calculateTaskPriorityScore(b) - this.calculateTaskPriorityScore(a))
                .slice(0, 20); // Limit to top 20 priority tasks
        }
        catch (error) {
            this.debugLog('[MCP] Error fetching CRM tasks:', error);
            // Fallback to extracting from NextStep if tasks API fails
            return [];
        }
    }
    // Extract priority tasks from Description fields (FALLBACK METHOD)
    extractPriorityTasks(leads) {
        const tasks = [];
        leads.forEach(lead => {
            const description = lead.Description || lead.description || '';
            const nextStep = lead.NextStep || lead.nextStep || '';
            // Parse structured tasks from description
            if (description.includes('TASK:')) {
                const taskMatches = description.match(/TASK:([^|]+)\|PRIORITY:([^|]+)(?:\|DUE:([^|]+))?/g);
                if (taskMatches) {
                    taskMatches.forEach((match) => {
                        const parts = match.split('|');
                        tasks.push({
                            task: parts[0].replace('TASK:', '').trim(),
                            priority: parts[1].replace('PRIORITY:', '').trim(),
                            due: parts[2] ? parts[2].replace('DUE:', '').trim() : 'No deadline',
                            lead: lead.FirstName ? `${lead.FirstName} ${lead.LastName || ''}`.trim() : lead.name,
                            company: lead.Company || lead.company
                        });
                    });
                }
            }
            // Generate tasks from nextStep if no structured tasks
            if (nextStep && !description.includes('TASK:')) {
                tasks.push({
                    task: nextStep,
                    priority: this.inferPriorityFromContent(nextStep + ' ' + description),
                    due: 'Soon',
                    lead: lead.FirstName ? `${lead.FirstName} ${lead.LastName || ''}`.trim() : lead.name,
                    company: lead.Company || lead.company
                });
            }
        });
        return tasks.sort((a, b) => {
            const priorityOrder = { 'urgent': 4, 'high': 3, 'medium': 2, 'low': 1 };
            const aPriority = priorityOrder[a.priority.toLowerCase()] || 1;
            const bPriority = priorityOrder[b.priority.toLowerCase()] || 1;
            return bPriority - aPriority;
        });
    }
    extractTimeFromNextStep(nextStep) {
        const timeRegex = /(\d{1,2}:\d{2}\s*(?:AM|PM)?|\d{1,2}\s*(?:AM|PM))/i;
        const match = nextStep.match(timeRegex);
        return match ? match[0] : 'Time TBD';
    }
    extractPriorityFromNextStep(nextStep) {
        if (!nextStep)
            return 'Medium';
        const lower = nextStep.toString().toLowerCase();
        if (lower.includes('urgent') || lower.includes('asap'))
            return 'Urgent';
        if (lower.includes('important') || lower.includes('priority'))
            return 'High';
        return 'Medium';
    }
    inferPriorityFromContent(content) {
        if (!content)
            return 'low';
        const lower = content.toString().toLowerCase();
        if (lower.includes('urgent') || lower.includes('asap') || lower.includes('hot'))
            return 'urgent';
        if (lower.includes('important') || lower.includes('qualified') || lower.includes('demo'))
            return 'high';
        if (lower.includes('follow') || lower.includes('check'))
            return 'medium';
        return 'low';
    }
    // Rosa Daily Briefing Formatter
    formatRosaDailyBriefing(data) {
        const { topLeads, upcomingMeetings, priorityTasks, greetingStyle, topLeadsCount } = data;
        // Generate greeting based on style
        let greeting = '';
        const currentTime = new Date().toLocaleTimeString('en-US', {
            hour: 'numeric',
            minute: '2-digit',
            hour12: true
        });
        switch (greetingStyle) {
            case 'rosa':
                greeting = `Hello - Good morning! Here's your day at a glance:\n• Top ${topLeadsCount} leads ranked by action score\n• Upcoming meetings\n• Key priority tasks\nWould you like details on your top leads?`;
                break;
            case 'casual':
                greeting = `Hey! Good morning! Ready to crush today? Here's what's on deck:`;
                break;
            case 'formal':
                greeting = `Good morning. Your daily sales briefing is ready for review.`;
                break;
            default:
                greeting = `Good morning! Here's your day at a glance:`;
        }
        // Format top leads in Rosa style
        const formattedTopLeads = topLeads.map((lead, index) => {
            const name = lead.FirstName ? `${lead.FirstName} ${lead.LastName || ''}`.trim() : lead.name || 'Unknown';
            const company = lead.Company || lead.company || 'Unknown Company';
            const actionScore = lead.actionScore || this.calculateRosaActionScore(lead);
            const category = lead.rosaCategory || this.determineRosaCategory(lead);
            return {
                rank: index + 1,
                name,
                company,
                action_score: actionScore,
                category,
                email: lead.Email || lead.email,
                phone: lead.Phone || lead.phone,
                title: lead.Title || lead.title,
                revenue: lead.AnnualRevenue || lead.annualRevenue,
                last_activity: lead.LastActivity || lead.lastActivity,
                next_step: lead.NextStep || lead.nextStep,
                rosa_summary: `${name} – ${company} (Action Score: ${actionScore}, ${category})`
            };
        });
        // Format upcoming meetings
        const formattedMeetings = upcomingMeetings.map((meeting) => ({
            person: meeting.person,
            company: meeting.company,
            context: meeting.context,
            priority: meeting.priority,
            estimated_time: meeting.estimated_time,
            summary: `${meeting.person} (${meeting.company}) - ${meeting.context}`
        }));
        // Format priority tasks
        const formattedTasks = priorityTasks.slice(0, 8).map((task) => ({
            task: task.task,
            priority: task.priority,
            due: task.due,
            lead: task.lead,
            company: task.company,
            summary: `${task.priority.toUpperCase()}: ${task.task} (${task.lead})`
        }));
        // Generate Rosa insights and recommendations
        const rosaInsights = this.generateRosaInsights(topLeads, upcomingMeetings, priorityTasks);
        // Create summary
        const summary = {
            total_leads_analyzed: topLeads.length,
            highest_action_score: topLeads.length > 0 ? Math.max(...topLeads.map((l) => l.actionScore || 0)) : 0,
            meetings_scheduled: formattedMeetings.length,
            urgent_tasks: formattedTasks.filter((t) => t.priority.toLowerCase() === 'urgent').length,
            top_recommendation: this.getTopRecommendation(topLeads, priorityTasks)
        };
        return {
            greeting,
            summary,
            topLeads: formattedTopLeads,
            upcomingMeetings: formattedMeetings,
            priorityTasks: formattedTasks,
            rosaInsights
        };
    }
    generateRosaInsights(topLeads, meetings, tasks) {
        const insights = {
            focus_recommendations: [],
            urgency_alerts: [],
            opportunity_highlights: [],
            productivity_tips: []
        };
        // Focus recommendations based on top leads
        if (topLeads.length > 0) {
            const topLead = topLeads[0];
            if (topLead.actionScore >= 90) {
                insights.focus_recommendations.push(`šŸ”„ PRIORITY: ${topLead.name} at ${topLead.company} has an action score of ${topLead.actionScore} - extremely high potential!`);
            }
            const hotLeads = topLeads.filter((l) => l.actionScore >= 85);
            if (hotLeads.length > 1) {
                insights.focus_recommendations.push(`šŸŽÆ You have ${hotLeads.length} leads with 85+ action scores - consider parallel outreach strategies.`);
            }
        }
        // Urgency alerts
        const urgentTasks = tasks.filter(t => t.priority.toLowerCase() === 'urgent');
        if (urgentTasks.length > 0) {
            insights.urgency_alerts.push(`āš ļø URGENT: ${urgentTasks.length} high-priority tasks need immediate attention.`);
        }
        const todaysMeetings = meetings.filter(m => (m.context && m.context.toLowerCase().includes('today')) ||
            (m.estimated_time && m.estimated_time !== 'Time TBD'));
        if (todaysMeetings.length > 0) {
            insights.urgency_alerts.push(`šŸ“… TODAY: ${todaysMeetings.length} meetings scheduled - prepare talking points.`);
        }
        // Opportunity highlights
        const qualifiedLeads = topLeads.filter((l) => (l.category && l.category.includes('Qualified')) ||
            (l.category && l.category.includes('Hot')));
        if (qualifiedLeads.length > 0) {
            insights.opportunity_highlights.push(`šŸ’° ${qualifiedLeads.length} qualified leads ready for closing activities.`);
        }
        // Productivity tips
        insights.productivity_tips.push('šŸš€ Start with your highest action score lead for maximum impact.');
        if (meetings.length > 0) {
            insights.productivity_tips.push('šŸ“‹ Use meeting prep tool for detailed conversation strategies.');
        }
        return insights;
    }
    getTopRecommendation(topLeads, tasks) {
        if (topLeads.length === 0)
            return 'Focus on lead generation and qualification activities.';
        const topLead = topLeads[0];
        const urgentTasks = tasks.filter(t => t.priority.toLowerCase() === 'urgent');
        if (urgentTasks.length > 0) {
            return `Handle urgent task: ${urgentTasks[0].task}, then focus on ${topLead.name} (Action Score: ${topLead.actionScore})`;
        }
        return `Focus on ${topLead.name} at ${topLead.company} - highest action score (${topLead.actionScore}) with strong potential.`;
    }
    // Helper methods for CRM tasks processing
    formatTaskDueDate(dueDate) {
        if (!dueDate)
            return 'No due date';
        try {
            const due = new Date(dueDate);
            const now = new Date();
            const diffDays = Math.ceil((due.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
            if (diffDays === 0)
                return 'Today';
            if (diffDays === 1)
                return 'Tomorrow';
            if (diffDays === -1)
                return 'Yesterday (Overdue)';
            if (diffDays < 0)
                return `${Math.abs(diffDays)} days overdue`;
            if (diffDays <= 7)
                return `${diffDays} days`;
            return due.toLocaleDateString();
        }
        catch (error) {
            return dueDate;
        }
    }
    // Google Maps Link Generation (using existing CRM address data)
    generateGoogleMapsLinks(address, options = {}) {
        const encodedAddress = encodeURIComponent(address.trim());
        const mapType = options.map_type || 'standard';
        const includeDirections = options.include_directions || false;
        const fromAddress = options.from_address;
        // Generate different Google Maps links
        const links = {
            // Standard Google Maps view
            standard_view: `https://www.google.com/maps/search/?api=1&query=${encodedAddress}`,
            // Direct location link (opens in Google Maps app if available)
            location_link: `https://maps.google.com/?q=${encodedAddress}`,
            // Satellite view
            satellite_view: `https://www.google.com/maps/search/?api=1&query=${encodedAddress}&map_action=map&basemap=satellite`,
            // Street view (if available)
            street_view: `https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${encodedAddress}`,
            // Embeddable map link
            embed_link: `https://www.google.com/maps/embed/v1/place?key=YOUR_API_KEY&q=${encodedAddress}`,
            // Mobile-friendly link
            mobile_link: `https://maps.google.com/maps?q=${encodedAddress}&hl=en&gl=us`
        };
        // Add directions link if requested
        if (includeDirections && fromAddress) {
            const encodedFromAddress = encodeURIComponent(fromAddress.trim());
            links.directions = `https://www.google.com/maps/dir/?api=1&origin=${encodedFromAddress}&destination=${encodedAddress}&travelmode=driving`;
            links.transit_directions = `https://www.google.com/maps/dir/?api=1&origin=${encodedFromAddress}&destination=${encodedAddress}&travelmode=transit`;
            links.walking_directions = `https://www.google.com/maps/dir/?api=1&origin=${encodedFromAddress}&destination=${encodedAddress}&travelmode=walking`;
        }
        // Generate additional information
        const info = {
            formatted_address: address,
            maps_search_query: encodedAddress,
            suggested_actions: [
                'Click "Standard View" to see the location on Google Maps',
                'Use "Mobile Link" to share with team members',
                includeDirections ? 'Get driving directions with the directions link' : 'Add from_address parameter for driving directions',
                'Use "Street View" to see the actual building (if available)'
            ],
            rosa_usage: {
                demo_scenario: 'Rosa can now say: "Here\'s the headquarters for Urban MartRetail Group on Google Maps. You can grab directions, check traffic, or share the location with your team."',
                integration_method: 'Uses existing CRM address field data - no external API required',
                supported_crm_fields: ['Street/address', 'City/city', 'State/state', 'PostalCode/zip', 'Country/country']
            }
        };
        return { links, info };
    }
    // Enhanced Meeting Prep with Location Integration
    enhanceMeetingPrepWithLocation(leadData, args) {
        const addressParts = [
            leadData.address,
            leadData.city,
            leadData.state,
            leadData.country
        ].filter(Boolean);
        if (addressParts.length > 0) {
            const fullAddress = addressParts.join(', ');
            const mapsData = this.generateGoogleMapsLinks(fullAddress);
            return {
                meeting_location: {
                    address: fullAddress,
                    google_maps_link: mapsData.links.standard_view,
                    mobile_maps_link: mapsData.links.mobile_link,
                    rosa_context: `Meeting location: ${fullAddress}. Google Maps link available for directions and traffic updates.`
                },
                location_talking_points: [
                    `I see you're located at ${fullAddress} - I can provide directions if needed`,
                    'I can share the Google Maps link with my team for easy reference',
                    'Let me know if you need any help with directions to your office'
                ]
            };
        }
        return null;
    }
    // DNC Compliance Checking using Fax field as Y/N flag
    checkDNCComplianceFromFaxField(contact) {
        // Get the Fax field value (case insensitive)
        const faxField = (contact.Fax || contact.fax || '').toString().trim().toLowerCase();
        this.debugLog('[MCP] Checking DNC via Fax field:', faxField);
        // If Fax field is empty, assume contact is allowed (default behavior)
        if (!faxField || faxField === '') {
            return {
                cleared: true,
                reason: 'Fax field is empty - no DNC restriction found (default: cleared)'
            };
        }
        // Check for DNC indicators (Y/Yes/True/1 = DO NOT CALL)
        const dncIndicators = ['y', 'yes', 'true', '1', 'dnc', 'do not call'];
        const clearIndicators = ['n', 'no', 'false', '0'];
        if (faxField && dncIndicators.includes(faxField)) {
            return {
                cleared: false,
                reason: `Fax field set to "${contact.Fax}" - Contact is on Do Not Call list`
            };
        }
        if (faxField && clearIndicators.includes(faxField)) {
            return {
                cleared: true,
                reason: `Fax field set to "${contact.Fax}" - Contact explicitly cleared for calling`
            };
        }
        // If Fax field has some other value, treat as cleared but note the unusual value
        return {
            cleared: true,
            reason: `Fax field contains "${contact.Fax}" - treating as cleared (non-standard value)`
        };
    }
    // Extract tasks from lead data when CRM tasks API is unavailable
    async extractTasksFromLeadData() {
        try {
            // Get leads data to extract task information from descriptions and next steps
            const leadsResult = await this.apiService.queryCRM({
                entity_type: 'leads',
                limit: 50,
                offset: 0,
                sort_order: 'desc'
            }, this.userToken);
            if (!leadsResult.success || !Array.isArray(leadsResult.data)) {
                return [];
            }
            const tasks = [];
            leadsResult.data.forEach((lead) => {
                const nextStep = (lead.NextStep || lead.nextStep || '').toString();
                const description = (lead.Description || lead.description || '').toString();
                const leadName = `${lead.FirstName || ''} ${lead.LastName || ''}`.trim();
                const company = lead.Company || lead.company;
                // Extract tasks from NextStep field
                if (nextStep && nextStep.length > 5) {
                    tasks.push({
                        id: `task_${lead.Id || lead.id}_nextstep`,
                        task: nextStep,
                        description: `Next step for ${leadName}`,
                        priority: this.inferPriorityFromContent(nextStep + ' ' + description),
                        status: 'Not Started',
                        due: 'Soon',
                        created: lead.CreatedDate || lead.createdDate || new Date().toISOString(),
                        lead_id: lead.Id || lead.id,
                        lead_name: leadName,
                        company: company,
                        crm_provider: 'NextStep Field',
                        rosa_summary: `${this.inferPriorityFromContent(nextStep).toUpperCase()}: ${nextStep} (${leadName})`
                    });
                }
                // Extract tasks from description if it contains action items
                if (description && description.length > 20) {
                    const actionKeywords = ['follow up', 'send', 'call', 'email', 'schedule', 'prepare', 'review'];
                    const hasActionKeyword = actionKeywords.some(keyword => description.toLowerCase().includes(keyword));
                    if (hasActionKeyword) {
                        // Extract first sentence that contains action keywords
                        const sentences = description.split(/[.!?]+/).filter((s) => s.trim().length > 10);
                        const actionSentence = sentences.find((sentence) => actionKeywords.some(keyword => sentence.toLowerCase().includes(keyword)));
                        if (actionSentence && actionSentence.length > 10) {
                            tasks.push({
                                id: `task_${lead.Id || lead.id}_description`,
                                task: actionSentence.trim(),
                                description: `Action item from lead description`,
                                priority: this.inferPriorityFromContent(actionSentence + ' ' + nextStep),
                                status: 'Identified',
                                due: 'To be scheduled',
                                created: lead.CreatedDate || lead.createdDate || new Date().toISOString(),
                                lead_id: lead.Id || lead.id,
                                lead_name: leadName,
                                company: company,
                                crm_provider: 'Description Field',
                                rosa_summary: `${this.inferPriorityFromContent(actionSentence).toUpperCase()}: ${actionSentence.substring(0, 50)}... (${leadName})`
                            });
                        }
                    }
                }
            });
            // Sort by priority and return top tasks
            return tasks
                .sort((a, b) => this.calculateTaskPriorityScore(b) - this.calculateTaskPriorityScore(a))
                .slice(0, 20);
        }
        catch (error) {
            this.debugLog('[MCP] Error extracting tasks from lead data:', error);
            return [];
        }
    }
    // Fetch activities for specific leads using related-objects endpoint
    async fetchActivitiesForLeads(leads) {
        try {
            this.debugLog('[MCP] Fetching activities for top leads using related-objects endpoint');
            const tasks = [];
            // Get activities for each top lead
            for (const lead of leads.slice(0, 3)) { // Limit to top 3 leads to avoid too many API calls
                try {
                    const leadId = lead.Id || lead.id;
                    if (!leadId)
                        continue;
                    // Call the related-objects endpoint for this specific lead
                    const response = await fetch(`${this.apiService.config.iriseller_api_url}/api/crm-connect/related-objects?entityType=Lead&entityId=${leadId}`, {
                        method: 'GET',
                        headers: {
                            'Authorization': `Bearer ${this.userToken}`,
                            'Content-Type': 'application/json',
                            'X-MCP-User-Email': this.extractEmailFromToken(this.userToken)
                        },
                        signal: AbortSignal.timeout(15000)
                    });
                    if (response.ok) {
                        const data = await response.json();
                        if (data.success && data.data && Array.isArray(data.data)) {
                            // Filter for tasks and activities
                            const leadActivities = data.data.filter((item) => item.type === 'Task' || item.type === 'Activity' || item.Subject);
                            leadActivities.forEach((activity) => {
                                tasks.push({
                                    id: activity.Id || activity.id,
                                    task: activity.Subject || activity.subject || 'Activity',
                                    description: activity.Description || activity.description || '',
                                    priority: this.normalizePriority(activity.Priority || 'Medium'),
                                    status: activity.Status || activity.status || 'Open',
                                    due: this.formatTaskDueDate(activity.ActivityDate || activity.DueDate),
                                    created: activity.CreatedDate || activity.createdDate,
                                    lead_id: leadId,
                                    lead_name: `${lead.FirstName || lead.firstName || ''} ${lead.LastName || lead.lastName || ''}`.trim(),
                                    company: lead.Company || lead.company,
                                    crm_provider: 'CRM Related Objects',
                                    rosa_summary: `${this.normalizePriority(activity.Priority || 'Medium').toUpperCase()}: ${activity.Subject || 'Activity'}`
                                });
                            });
                        }
                    }
                }
                catch (leadError) {
                    this.debugLog(`[MCP] Error fetching activities for lead ${lead.FirstName} ${lead.LastName}:`, leadError);
                }
            }
            return tasks.sort((a, b) => this.calculateTaskPriorityScore(b) - this.calculateTaskPriorityScore(a));
        }
        catch (error) {
            this.debugLog('[MCP] Error fetching activities for leads:', error);
            return [];
        }
    }
    // Helper to extract email from JWT token
    extractEmailFromToken(token) {
        if (!token)
            return '';
        try {
            const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
            return payload.email || '';
        }
        catch (error) {
            return '';
        }
    }
    // Fetch activities from CRM using the same pattern as other CRM Connect calls
    async fetchActivitiesFromCRM() {
        try {
            this.debugLog('[MCP] Fetching priority tasks using same auth pattern as leads API');
            // Use the exact same approach as the working queryCRMTasks method
            const tasksResult = await this.apiService.queryCRMTasks({
                limit: 50,
                offset: 0,
                sort_by: 'priority',
                sort_order: 'desc'
            }, this.userToken);
            if (!tasksResult.success) {
                this.debugLog(`[MCP] CRM tasks API failed: ${tasksResult.error}`);
                return [];
            }
            if (!Array.isArray(tasksResult.data) || tasksResult.data.length === 0) {
                this.debugLog('[MCP] No tasks found in CRM tasks API response');
                return [];
            }
            this.debugLog(`[MCP] Found ${tasksResult.data.length} tasks from CRM using same auth as leads`);
            const tasks = tasksResult.data.map((task) => ({
                id: task.id,
                task: task.subject || task.Subject || 'CRM Task',
                description: task.description || task.Description || '',
                priority: this.normalizePriority(task.priority || task.Priority || 'Medium'),
                status: task.status || task.Status || 'Open',
                due: this.formatTaskDueDate(task.dueDate || task.ActivityDate),
                created: task.createdDate || task.CreatedDate,
                lead_id: task.relatedToId || task.whoId,
                lead_name: task.relatedToName || 'Unknown Contact',
                company: task.relatedToCompany || 'Unknown Company',
                crm_provider: 'CRM Tasks API (Same Auth as Leads)',
                rosa_summary: `${this.normalizePriority(task.priority || task.Priority || 'Medium').toUpperCase()}: ${task.subject || task.Subject || 'Task'}`
            }));
            return tasks
                .sort((a, b) => this.calculateTaskPriorityScore(b) - this.calculateTaskPriorityScore(a))
                .slice(0, 20);
        }
        catch (error) {
            this.debugLog('[MCP] Error fetching tasks from CRM:', error);
            return [];
        }
    }
}