<?php
/**
 * Conversation Compressor.
 *
 * Compresses raw conversation transcripts into structured JSON schema,
 * achieving 60-70% token reduction for AI processing cost optimization.
 *
 * @link       https://github.com/sitestaffr/sitestaffr-plugin
 * @since      1.0.0
 *
 * @package    SiteStaffr
 * @subpackage SiteStaffr/includes
 */

/**
 * Conversation Compressor class.
 *
 * Extracts essential information from conversation transcripts and compresses
 * them into a structured JSON format following the conversation memory schema.
 * This reduces token usage for summary and follow-up generation by ~60-70%.
 *
 * @since      1.0.0
 * @package    SiteStaffr
 * @subpackage SiteStaffr/includes
 * @author     SiteStaffr
 */
class SiteStaffr_Conversation_Compressor {

    /**
     * Compress conversation transcript into structured JSON schema.
     *
     * Main method that takes raw transcript and call data, extracts all
     * critical information, and returns compressed structured format.
     *
     * @since    1.0.0
     * @param    array  $transcripts  Array of transcript objects from database.
     * @param    array  $call_data    Call metadata (id, timestamp, duration, caller_id).
     * @return   array                Compressed conversation structure.
     */
    public function compress_transcript( $transcripts, $call_data ) {
        // Combine all transcript messages into full conversation text
        $full_conversation = $this->build_full_conversation( $transcripts );

        // Build compressed structure
        $compressed = array(
            'call_metadata' => array(
                'call_id'          => isset( $call_data['call_id'] ) ? strval( $call_data['call_id'] ) : '',
                'timestamp'        => isset( $call_data['timestamp'] ) ? $call_data['timestamp'] : gmdate( 'c' ),
                'duration_seconds' => isset( $call_data['duration'] ) ? intval( $call_data['duration'] ) : 0,
                'caller_id'        => isset( $call_data['caller_id'] ) ? $call_data['caller_id'] : null,
            ),
            'caller_profile'          => $this->extract_caller_profile( $full_conversation ),
            'call_intent'             => $this->extract_call_intent( $full_conversation ),
            'information_collected'   => $this->extract_information_collected( $full_conversation ),
            'caller_constraints'      => $this->extract_constraints( $full_conversation ),
            'conversation_quality'    => $this->analyze_conversation_quality( $full_conversation ),
            'ai_actions_taken'        => $this->extract_ai_actions( $transcripts ),
            'escalation_indicators'   => $this->detect_escalation( $full_conversation ),
            'follow_up_context'       => $this->build_follow_up_context( $full_conversation ),
        );

        // Generate ultra-concise summary text
        $compressed['conversation_summary_text'] = $this->generate_summary_text( $compressed );

        return $compressed;
    }

    /**
     * Build full conversation text from transcript array.
     *
     * @since    1.0.0
     * @param    array  $transcripts  Array of transcript objects.
     * @return   string               Combined conversation text.
     */
    private function build_full_conversation( $transcripts ) {
        $conversation = '';

        foreach ( $transcripts as $transcript ) {
            $speaker = ( 'caller' === $transcript->speaker ) ? 'Visitor' : 'AI';
            $message = isset( $transcript->message_text ) ? $transcript->message_text : '';
            $conversation .= "$speaker: $message\n";
        }

        return $conversation;
    }

    /**
     * Extract caller profile (name, phone, email, confirmations).
     *
     * @since    1.0.0
     * @param    string  $conversation  Full conversation text.
     * @return   array                  Caller profile structure.
     */
    public function extract_caller_profile( $conversation ) {
        $profile = array(
            'name'  => null,
            'phone' => null,
            'email' => null,
            'confirmed' => array(
                'name'  => false,
                'phone' => false,
                'email' => false,
            ),
        );

        // Extract name
        if ( preg_match( '/(?:my name is|this is|i\'m|name\'s)\s+([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)/i', $conversation, $matches ) ) {
            $profile['name'] = trim( $matches[1] );
            // Check if AI confirmed it back
            if ( stripos( $conversation, $profile['name'] ) !== false && stripos( $conversation, 'AI:' ) !== false ) {
                $profile['confirmed']['name'] = true;
            }
        }

        // Extract phone number (various formats)
        if ( preg_match( '/\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}/', $conversation, $matches ) ) {
            $profile['phone'] = trim( $matches[0] );
            // Check if AI confirmed it back
            if ( stripos( $conversation, $profile['phone'] ) !== false && preg_match( '/AI:.*?' . preg_quote( $profile['phone'], '/' ) . '/i', $conversation ) ) {
                $profile['confirmed']['phone'] = true;
            }
        }

        // Extract email
        if ( preg_match( '/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/', $conversation, $matches ) ) {
            $profile['email'] = trim( $matches[0] );
            // Check if AI confirmed it back
            if ( stripos( $conversation, $profile['email'] ) !== false && preg_match( '/AI:.*?' . preg_quote( $profile['email'], '/' ) . '/i', $conversation ) ) {
                $profile['confirmed']['email'] = true;
            }
        }

        return $profile;
    }

    /**
     * Extract call intent (primary purpose, keywords).
     *
     * @since    1.0.0
     * @param    string  $conversation  Full conversation text.
     * @return   array                  Call intent structure.
     */
    public function extract_call_intent( $conversation ) {
        $intent = array(
            'primary_purpose'    => 'unknown',
            'secondary_purposes' => array(),
            'keywords'           => array(),
        );

        $conversation_lower = strtolower( $conversation );

        // Intent keyword mapping (priority order)
        $intent_patterns = array(
            'emergency_service'   => array( 'urgent', 'emergency', 'asap', 'immediately', 'right now' ),
            'spam_sales'          => array( 'not interested', 'remove from list', 'stop calling', 'selling' ),
            'complaint'           => array( 'complain', 'terrible', 'unacceptable', 'frustrated', 'upset' ),
            'appointment_booking' => array( 'schedule', 'appointment', 'book', 'meeting', 'reservation' ),
            'callback_request'    => array( 'call me back', 'callback', 'call back', 'reach out', 'contact me' ),
            'email_request'       => array( 'email me', 'send email', 'email information', 'send info' ),
            'pricing_inquiry'     => array( 'how much', 'cost', 'price', 'pricing', 'quote', 'estimate' ),
            'service_inquiry'     => array( 'services', 'what do you offer', 'information about', 'details about' ),
            'hours_inquiry'       => array( 'hours', 'open', 'closed', 'available', 'when are you' ),
            'location_inquiry'    => array( 'where are you', 'location', 'address', 'directions' ),
            'wrong_number'        => array( 'wrong number', 'not who', 'mistake', 'sorry' ),
        );

        // Detect primary purpose (first match wins)
        foreach ( $intent_patterns as $purpose => $keywords ) {
            foreach ( $keywords as $keyword ) {
                if ( stripos( $conversation_lower, $keyword ) !== false ) {
                    $intent['primary_purpose'] = $purpose;
                    $intent['keywords'][] = $keyword;
                    break 2; // Exit both loops
                }
            }
        }

        // If still unknown, try general inquiry
        if ( 'unknown' === $intent['primary_purpose'] ) {
            if ( preg_match( '/\b(question|info|help|wondering|curious)\b/i', $conversation ) ) {
                $intent['primary_purpose'] = 'general_information';
            }
        }

        // Extract additional keywords for context
        $keyword_patterns = array( 'plumbing', 'electrical', 'repair', 'install', 'service', 'dentist', 'lawyer', 'real estate' );
        foreach ( $keyword_patterns as $keyword ) {
            if ( stripos( $conversation_lower, $keyword ) !== false ) {
                $intent['keywords'][] = $keyword;
            }
        }

        // Remove duplicate keywords
        $intent['keywords'] = array_unique( $intent['keywords'] );

        return $intent;
    }

    /**
     * Extract information collected during call.
     *
     * @since    1.0.0
     * @param    string  $conversation  Full conversation text.
     * @return   array                  Information collected structure.
     */
    private function extract_information_collected( $conversation ) {
        $info = array(
            'service_interest'     => null,
            'appointment_requested' => array(
                'requested'   => false,
                'date_time'   => null,
                'flexibility' => null,
            ),
            'pricing_inquiry'      => false,
            'location_inquiry'     => false,
            'hours_inquiry'        => false,
            'custom_fields'        => array(),
        );

        $conversation_lower = strtolower( $conversation );

        // Detect service interest
        $service_patterns = array( 'plumbing', 'electrical', 'hvac', 'roofing', 'dental', 'legal', 'real estate', 'repair', 'installation' );
        foreach ( $service_patterns as $service ) {
            if ( stripos( $conversation_lower, $service ) !== false ) {
                $info['service_interest'] = $service;
                break;
            }
        }

        // Detect appointment request
        if ( preg_match( '/\b(appointment|schedule|book|meeting|friday|monday|tuesday|wednesday|thursday|saturday|sunday|tomorrow|next week)\b/i', $conversation ) ) {
            $info['appointment_requested']['requested'] = true;

            // Extract timing
            if ( preg_match( '/\b(friday|monday|tuesday|wednesday|thursday|saturday|sunday|tomorrow|next week|this week|today)\b/i', $conversation, $matches ) ) {
                $info['appointment_requested']['date_time'] = $matches[1];
            }

            // Extract flexibility
            if ( preg_match( '/\b(anytime|flexible|morning|afternoon|evening|after \d+|before \d+)\b/i', $conversation, $matches ) ) {
                $info['appointment_requested']['flexibility'] = $matches[0];
            }
        }

        // Detect inquiry types
        $info['pricing_inquiry']  = (bool) preg_match( '/\b(price|cost|how much|quote|estimate)\b/i', $conversation );
        $info['location_inquiry'] = (bool) preg_match( '/\b(where|location|address|directions)\b/i', $conversation );
        $info['hours_inquiry']    = (bool) preg_match( '/\b(hours|open|closed|available|when are you)\b/i', $conversation );

        return $info;
    }

    /**
     * Extract caller constraints (timing, budget, special requirements).
     *
     * @since    1.0.0
     * @param    string  $conversation  Full conversation text.
     * @return   array                  Caller constraints structure.
     */
    public function extract_constraints( $conversation ) {
        $constraints = array(
            'timing_preferences'   => null,
            'budget_constraints'   => null,
            'special_requirements' => array(),
            'urgency_level'        => 'low',
        );

        // Extract timing preferences
        if ( preg_match( '/\b(after \d+ ?(?:am|pm)|before \d+ ?(?:am|pm)|morning|afternoon|evening|weekday|weekend)\b/i', $conversation, $matches ) ) {
            $constraints['timing_preferences'] = trim( $matches[0] );
        }

        // Extract budget constraints
        if ( preg_match( '/\b(under \$?\d+|budget|affordable|cheap|expensive|premium)\b/i', $conversation, $matches ) ) {
            $constraints['budget_constraints'] = trim( $matches[0] );
        }

        // Detect urgency
        if ( preg_match( '/\b(urgent|emergency|asap|immediately|right now)\b/i', $conversation ) ) {
            $constraints['urgency_level'] = 'emergency';
        } elseif ( preg_match( '/\b(soon|quickly|today|this week)\b/i', $conversation ) ) {
            $constraints['urgency_level'] = 'high';
        } elseif ( preg_match( '/\b(sometime|eventually|no rush|whenever)\b/i', $conversation ) ) {
            $constraints['urgency_level'] = 'low';
        } else {
            $constraints['urgency_level'] = 'medium';
        }

        return $constraints;
    }

    /**
     * Analyze conversation quality (sentiment, issues).
     *
     * @since    1.0.0
     * @param    string  $conversation  Full conversation text.
     * @return   array                  Conversation quality metrics.
     */
    private function analyze_conversation_quality( $conversation ) {
        $quality = array(
            'sentiment'            => 'neutral',
            'clarity_issues'       => false,
            'language_barrier'     => false,
            'repetitive_questions' => false,
            'off_topic_attempts'   => false,
        );

        $conversation_lower = strtolower( $conversation );

        // Detect sentiment
        $angry_keywords   = array( 'angry', 'frustrated', 'upset', 'terrible', 'awful', 'unacceptable' );
        $positive_keywords = array( 'thank', 'great', 'perfect', 'excellent', 'appreciate' );

        $angry_count   = 0;
        $positive_count = 0;

        foreach ( $angry_keywords as $keyword ) {
            if ( stripos( $conversation_lower, $keyword ) !== false ) {
                $angry_count++;
            }
        }

        foreach ( $positive_keywords as $keyword ) {
            if ( stripos( $conversation_lower, $keyword ) !== false ) {
                $positive_count++;
            }
        }

        if ( $angry_count > 0 ) {
            $quality['sentiment'] = 'angry';
        } elseif ( $positive_count > 2 ) {
            $quality['sentiment'] = 'positive';
        } elseif ( $positive_count > 0 ) {
            $quality['sentiment'] = 'neutral';
        } else {
            $quality['sentiment'] = 'neutral';
        }

        // Detect clarity issues
        if ( preg_match( '/\b(pardon|what|repeat|didn\'t hear|unclear)\b/i', $conversation ) ) {
            $quality['clarity_issues'] = true;
        }

        // Detect language barrier
        if ( preg_match( '/\b(no speak english|no understand|translate|interpreter)\b/i', $conversation ) ) {
            $quality['language_barrier'] = true;
        }

        // Detect repetitive questions (same question asked multiple times)
        $ai_messages = array();
        $lines = explode( "\n", $conversation );
        foreach ( $lines as $line ) {
            if ( stripos( $line, 'AI:' ) === 0 ) {
                $ai_messages[] = $line;
            }
        }

        // Check for duplicate AI messages (sign of repetition)
        if ( count( $ai_messages ) !== count( array_unique( $ai_messages ) ) ) {
            $quality['repetitive_questions'] = true;
        }

        return $quality;
    }

    /**
     * Extract AI actions taken during conversation.
     *
     * @since    1.0.0
     * @param    array  $transcripts  Array of transcript objects.
     * @return   array                AI actions structure.
     */
    private function extract_ai_actions( $transcripts ) {
        $actions = array(
            'information_provided' => array(),
            'confirmations_made'   => array(),
            'redirections'         => array(),
        );

        foreach ( $transcripts as $transcript ) {
            if ( 'ai' === $transcript->speaker ) {
                $message = isset( $transcript->message_text ) ? $transcript->message_text : '';

                // Detect information provided
                if ( preg_match( '/\b(our hours are|we offer|we provide|located at|costs|price is)\b/i', $message ) ) {
                    $actions['information_provided'][] = trim( $message );
                }

                // Detect confirmations
                if ( preg_match( '/\b(confirm|let me make sure|is that correct|sounds like)\b/i', $message ) ) {
                    $actions['confirmations_made'][] = trim( $message );
                }

                // Detect redirections
                if ( preg_match( '/\b(actually|however|instead|message service|answering service|not able to)\b/i', $message ) ) {
                    $actions['redirections'][] = trim( $message );
                }
            }
        }

        return $actions;
    }

    /**
     * Detect escalation indicators (angry, emergency, spam).
     *
     * @since    1.0.0
     * @param    string  $conversation  Full conversation text.
     * @return   array                  Escalation indicators structure.
     */
    public function detect_escalation( $conversation ) {
        $escalation = array(
            'angry_customer'   => false,
            'emergency_service' => false,
            'complaint'        => false,
            'confusion'        => false,
            'spam_detected'    => false,
        );

        $conversation_lower = strtolower( $conversation );

        // Detect angry customer
        if ( preg_match( '/\b(angry|frustrated|upset|terrible|awful|unacceptable|ridiculous)\b/i', $conversation ) ) {
            $escalation['angry_customer'] = true;
        }

        // Detect emergency
        if ( preg_match( '/\b(urgent|emergency|asap|immediately|critical|serious)\b/i', $conversation ) ) {
            $escalation['emergency_service'] = true;
        }

        // Detect complaint
        if ( preg_match( '/\b(complain|complaint|problem|issue|not happy|disappointed)\b/i', $conversation ) ) {
            $escalation['complaint'] = true;
        }

        // Detect confusion
        if ( preg_match( '/\b(confused|don\'t understand|what do you mean|who is this|wrong number)\b/i', $conversation ) ) {
            $escalation['confusion'] = true;
        }

        // Detect spam
        if ( preg_match( '/\b(not interested|remove from list|stop calling|take me off|do not call)\b/i', $conversation ) ) {
            $escalation['spam_detected'] = true;
        }

        return $escalation;
    }

    /**
     * Build follow-up context (actions required).
     *
     * @since    1.0.0
     * @param    string  $conversation  Full conversation text.
     * @return   array                  Follow-up context structure.
     */
    private function build_follow_up_context( $conversation ) {
        $context = array(
            'primary_action'       => 'Review call details',
            'action_items'         => array(),
            'callback_needed'      => false,
            'email_needed'         => false,
            'information_to_send'  => array(),
        );

        $conversation_lower = strtolower( $conversation );

        // Detect callback needed
        if ( preg_match( '/\b(call me back|callback|call back|reach out|contact me)\b/i', $conversation ) ) {
            $context['callback_needed'] = true;
            $context['primary_action'] = 'Call customer back';
            $context['action_items'][] = 'Return call to discuss request';
        }

        // Detect email needed
        if ( preg_match( '/\b(email me|send email|email information|send info)\b/i', $conversation ) ) {
            $context['email_needed'] = true;
            $context['primary_action'] = 'Send email to customer';
            $context['action_items'][] = 'Email requested information';

            // Extract what to send
            if ( preg_match( '/\b(pricing|price list|hours|location|services|details|information)\b/i', $conversation, $matches ) ) {
                $context['information_to_send'][] = $matches[0];
            }
        }

        // Detect appointment scheduling
        if ( preg_match( '/\b(appointment|schedule|book|meeting)\b/i', $conversation ) ) {
            $context['primary_action'] = 'Schedule appointment';
            $context['action_items'][] = 'Contact customer to schedule appointment';
        }

        // If no specific action detected, set generic
        if ( empty( $context['action_items'] ) ) {
            $context['action_items'][] = 'Review call and determine appropriate response';
        }

        return $context;
    }

    /**
     * Generate 1-2 sentence summary text.
     *
     * @since    1.0.0
     * @param    array  $compressed  The compressed conversation structure.
     * @return   string              Ultra-concise summary text.
     */
    public function generate_summary_text( $compressed ) {
        $caller_name = isset( $compressed['caller_profile']['name'] ) ? $compressed['caller_profile']['name'] : 'Visitor';
        $purpose     = isset( $compressed['call_intent']['primary_purpose'] ) ? $compressed['call_intent']['primary_purpose'] : 'unknown';

        // Build summary based on intent
        $summary = '';

        switch ( $purpose ) {
            case 'callback_request':
                $phone = isset( $compressed['caller_profile']['phone'] ) ? $compressed['caller_profile']['phone'] : 'unknown number';
                $timing = isset( $compressed['caller_constraints']['timing_preferences'] ) ? $compressed['caller_constraints']['timing_preferences'] : '';
                $summary = "$caller_name requested callback at $phone";
                if ( $timing ) {
                    $summary .= " $timing";
                }
                $summary .= '.';
                break;

            case 'email_request':
                $email = isset( $compressed['caller_profile']['email'] ) ? $compressed['caller_profile']['email'] : 'unknown email';
                $summary = "$caller_name requested information via email at $email.";
                break;

            case 'appointment_booking':
                $date = isset( $compressed['information_collected']['appointment_requested']['date_time'] ) ? $compressed['information_collected']['appointment_requested']['date_time'] : 'unspecified time';
                $summary = "$caller_name requested appointment for $date.";
                break;

            case 'emergency_service':
                $summary = "URGENT: $caller_name needs immediate assistance.";
                break;

            case 'spam_sales':
                $summary = "Spam/sales call detected. No action required.";
                break;

            case 'wrong_number':
                $summary = "Wrong number. No action required.";
                break;

            default:
                $keywords = isset( $compressed['call_intent']['keywords'] ) ? implode( ', ', array_slice( $compressed['call_intent']['keywords'], 0, 3 ) ) : '';
                $summary = "$caller_name called";
                if ( $keywords ) {
                    $summary .= " regarding $keywords";
                }
                $summary .= '.';
                break;
        }

        return $summary;
    }

    /**
     * Test method to verify compression works.
     *
     * Creates a sample transcript and compresses it, returning both
     * the original and compressed versions for comparison.
     *
     * @since    1.0.0
     * @return   array  Test results with original and compressed data.
     */
    public function run_test() {
        // Create sample transcript data
        $sample_transcripts = array(
            (object) array(
                'speaker' => 'ai',
                'message' => 'Hello! Thanks for calling. How can I help you today?',
            ),
            (object) array(
                'speaker' => 'caller',
                'message' => 'Hi, I need someone to call me back about your services.',
            ),
            (object) array(
                'speaker' => 'ai',
                'message' => 'I\'d be happy to arrange that. May I have your name?',
            ),
            (object) array(
                'speaker' => 'caller',
                'message' => 'My name is John Smith.',
            ),
            (object) array(
                'speaker' => 'ai',
                'message' => 'Thank you, John! What\'s the best number to reach you at?',
            ),
            (object) array(
                'speaker' => 'caller',
                'message' => '555-1234, anytime after 3pm works.',
            ),
            (object) array(
                'speaker' => 'ai',
                'message' => 'Perfect! So I have John Smith at 555-1234, and you\'d prefer a callback after 3pm. Is that correct?',
            ),
            (object) array(
                'speaker' => 'caller',
                'message' => 'Yes, that\'s right.',
            ),
        );

        $sample_call_data = array(
            'call_id'   => 'TEST123',
            'timestamp' => gmdate( 'c' ),
            'duration'  => 60,
            'caller_id' => '+15551234567',
        );

        // Run compression
        $compressed = $this->compress_transcript( $sample_transcripts, $sample_call_data );

        // Calculate token estimates (rough approximation)
        $original_text = $this->build_full_conversation( $sample_transcripts );
        $original_tokens = str_word_count( $original_text ) * 1.3; // Rough estimate
        $compressed_json = wp_json_encode( $compressed );
        $compressed_tokens = str_word_count( $compressed_json ) * 1.3; // Rough estimate
        $reduction = round( ( ( $original_tokens - $compressed_tokens ) / $original_tokens ) * 100, 1 );

        return array(
            'success'           => true,
            'original_text'     => $original_text,
            'original_tokens'   => round( $original_tokens ),
            'compressed_json'   => $compressed,
            'compressed_tokens' => round( $compressed_tokens ),
            'reduction_percent' => $reduction,
        );
    }
}

