<?php

/**
 * Servicio de Procesamiento de Lenguaje Natural (NLP)
 * Detecta intenciones y palabras clave en los mensajes
 */

class NLPService
{
    private $db;
    
    public function __construct()
    {
        $this->db = Database::getInstance();
    }
    
    /**
     * Procesa un mensaje y encuentra la mejor respuesta automática
     * @param string $message
     * @param string $phoneNumber
     * @return array|null ['type' => 'autoresponse|menu|transfer', 'data' => array]
     */
    public function processMessage(string $message, string $phoneNumber): ?array
    {
        // Normalizar mensaje
        $normalizedMessage = $this->normalizeText($message);
        
        // 1. Verificar si es una opción de menú numérica
        if (is_numeric(trim($message))) {
            return $this->processMenuOption($message, $phoneNumber);
        }
        
        // 2. Buscar respuesta automática por palabra clave
        $response = $this->findAutoResponse($normalizedMessage);
        
        if ($response) {
            return $this->formatResponse($response);
        }
        
        // 3. Si no encuentra nada, devolver respuesta por defecto
        return $this->getDefaultResponse();
    }
    
    /**
     * Normaliza el texto para comparación
     * @param string $text
     * @return string
     */
    private function normalizeText(string $text): string
    {
        // Convertir a minúsculas
        $text = mb_strtolower($text, 'UTF-8');
        
        // Remover acentos
        $text = $this->removeAccents($text);
        
        // Remover emojis y caracteres especiales (excepto espacios y comas)
        $text = preg_replace('/[^\p{L}\p{N}\s,]/u', '', $text);
        
        // Remover espacios extras
        $text = preg_replace('/\s+/', ' ', $text);
        
        return trim($text);
    }
    
    /**
     * Remueve acentos de un texto
     * @param string $text
     * @return string
     */
    private function removeAccents(string $text): string
    {
        $accents = [
            'á' => 'a', 'é' => 'e', 'í' => 'i', 'ó' => 'o', 'ú' => 'u',
            'Á' => 'a', 'É' => 'e', 'Í' => 'i', 'Ó' => 'o', 'Ú' => 'u',
            'ñ' => 'n', 'Ñ' => 'n', 'ü' => 'u', 'Ü' => 'u'
        ];
        
        return strtr($text, $accents);
    }
    
    /**
     * Busca la mejor respuesta automática para el mensaje
     * @param string $normalizedMessage
     * @return array|null
     */
    private function findAutoResponse(string $normalizedMessage): ?array
    {
        // Obtener todas las respuestas automáticas activas ordenadas por prioridad
        $stmt = $this->db->prepare("
            SELECT * FROM autoresponses 
            WHERE is_active = 1 
            AND trigger_type != 'welcome'
            ORDER BY priority DESC, id ASC
        ");
        $stmt->execute();
        $autoResponses = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        foreach ($autoResponses as $response) {
            if ($this->matchesTrigger($normalizedMessage, $response)) {
                return $response;
            }
        }
        
        return null;
    }
    
    /**
     * Verifica si un mensaje coincide con un trigger
     * @param string $message
     * @param array $response
     * @return bool
     */
    private function matchesTrigger(string $message, array $response): bool
    {
        $triggerType = $response['trigger_type'];
        $triggerValue = $response['trigger_value'];
        
        switch ($triggerType) {
            case 'keyword':
                return $this->matchesKeywords($message, $triggerValue);
                
            case 'contains':
                return $this->containsPhrase($message, $triggerValue);
                
            case 'exact':
                return $this->exactMatch($message, $triggerValue);
                
            case 'default':
                return true; // Siempre coincide (pero tiene prioridad baja)
                
            default:
                return false;
        }
    }
    
    /**
     * Verifica si el mensaje contiene alguna palabra clave
     * @param string $message
     * @param string $keywordsString Palabras separadas por comas
     * @return bool
     */
    private function matchesKeywords(string $message, string $keywordsString): bool
    {
        if (empty($keywordsString)) {
            return false;
        }
        
        $keywords = array_map('trim', explode(',', $keywordsString));
        $keywords = array_map([$this, 'normalizeText'], $keywords);
        
        $messageWords = explode(' ', $message);
        
        foreach ($keywords as $keyword) {
            // Verificar si la palabra clave completa está en el mensaje
            if (strpos($message, $keyword) !== false) {
                return true;
            }
            
            // O si alguna palabra del mensaje coincide exactamente
            if (in_array($keyword, $messageWords)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Verifica si el mensaje contiene una frase
     * @param string $message
     * @param string $phrase
     * @return bool
     */
    private function containsPhrase(string $message, string $phrase): bool
    {
        $normalizedPhrase = $this->normalizeText($phrase);
        return strpos($message, $normalizedPhrase) !== false;
    }
    
    /**
     * Verifica coincidencia exacta
     * @param string $message
     * @param string $exact
     * @return bool
     */
    private function exactMatch(string $message, string $exact): bool
    {
        $normalizedExact = $this->normalizeText($exact);
        return $message === $normalizedExact;
    }
    
    /**
     * Procesa una opción numérica de menú
     * @param string $option
     * @param string $phoneNumber
     * @return array|null
     */
    private function processMenuOption(string $option, string $phoneNumber): ?array
    {
        $stateService = new ConversationStateService();
        $currentMenuId = $stateService->getCurrentMenuId($phoneNumber);
        
        if (!$currentMenuId) {
            // Si no hay menú actual, buscar el menú principal
            $stmt = $this->db->prepare("
                SELECT id FROM menus 
                WHERE menu_key = 'lab_menu_principal' 
                AND status = 'active'
                LIMIT 1
            ");
            $stmt->execute();
            $menu = $stmt->fetch(PDO::FETCH_ASSOC);
            $currentMenuId = $menu ? $menu['id'] : null;
        }
        
        if (!$currentMenuId) {
            return null;
        }
        
        // Buscar la opción en el menú actual
        $stmt = $this->db->prepare("
            SELECT * FROM menu_options 
            WHERE menu_id = ? 
            AND option_key = ? 
            AND is_active = 1
            LIMIT 1
        ");
        $stmt->execute([$currentMenuId, trim($option)]);
        $menuOption = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$menuOption) {
            return null;
        }
        
        return $this->formatMenuOption($menuOption);
    }
    
    /**
     * Formatea una respuesta automática
     * @param array $response
     * @return array
     */
    private function formatResponse(array $response): array
    {
        $result = [
            'type' => $response['response_type'],
            'priority' => $response['priority']
        ];
        
        switch ($response['response_type']) {
            case 'text':
                $result['message'] = $response['response_text'];
                break;
                
            case 'menu':
                $result['menu_id'] = $response['menu_id'];
                break;
                
            case 'template':
                $result['template_name'] = $response['template_name'] ?? null;
                break;
                
            case 'transfer':
                $result['message'] = $response['response_text'];
                $result['transfer'] = true;
                break;
        }
        
        return $result;
    }
    
    /**
     * Formatea una opción de menú
     * @param array $menuOption
     * @return array
     */
    private function formatMenuOption(array $menuOption): array
    {
        $result = [
            'type' => $menuOption['response_type']
        ];
        
        switch ($menuOption['response_type']) {
            case 'text':
                $result['message'] = $menuOption['response_text'];
                break;
                
            case 'menu':
                $result['menu_id'] = $menuOption['next_menu_id'];
                break;
                
            case 'transfer':
                $result['message'] = $menuOption['response_text'];
                $result['transfer'] = true;
                break;
        }
        
        return $result;
    }
    
    /**
     * Obtiene la respuesta por defecto
     * @return array
     */
    private function getDefaultResponse(): array
    {
        $stmt = $this->db->prepare("
            SELECT * FROM autoresponses 
            WHERE trigger_type = 'default' 
            AND is_active = 1
            ORDER BY priority DESC
            LIMIT 1
        ");
        $stmt->execute();
        $response = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($response) {
            return $this->formatResponse($response);
        }
        
        // Respuesta fallback hardcoded
        return [
            'type' => 'text',
            'message' => "❓ No he comprendido tu mensaje.\n\nEscribe MENÚ para ver las opciones disponibles."
        ];
    }
    
    /**
     * Detecta el sentimiento del mensaje (básico)
     * @param string $message
     * @return string positive|negative|neutral
     */
    public function detectSentiment(string $message): string
    {
        $normalized = $this->normalizeText($message);
        
        // Palabras positivas
        $positiveWords = ['gracias', 'excelente', 'perfecto', 'bien', 'bueno', 'genial', 'ok', 'vale'];
        
        // Palabras negativas / frustración
        $negativeWords = ['no funciona', 'problema', 'error', 'mal', 'ayuda', 'no entiendo', 'dificil', 'complicado'];
        
        $positiveCount = 0;
        $negativeCount = 0;
        
        foreach ($positiveWords as $word) {
            if (strpos($normalized, $word) !== false) {
                $positiveCount++;
            }
        }
        
        foreach ($negativeWords as $word) {
            if (strpos($normalized, $word) !== false) {
                $negativeCount++;
            }
        }
        
        if ($negativeCount > $positiveCount) {
            return 'negative';
        } elseif ($positiveCount > 0) {
            return 'positive';
        }
        
        return 'neutral';
    }
    
    /**
     * Determina si el mensaje indica frustración (para escalar a asesor)
     * @param string $message
     * @return bool
     */
    public function detectsFrustration(string $message): bool
    {
        return $this->detectSentiment($message) === 'negative';
    }
}
