<?php

/**
 * Bot Service Mejorado - Laboratorio Ximena Caicedo
 * Integra todos los flujos y servicios del chatbot
 * Fecha: 13 de enero de 2026
 */

require_once __DIR__ . '/WhatsAppService.php';
require_once __DIR__ . '/BusinessHoursService.php';
require_once __DIR__ . '/ConversationStateService.php';
require_once __DIR__ . '/NLPService.php';
require_once __DIR__ . '/MenuService.php';

class BotServiceLab
{
    private $db;
    private $whatsappService;
    private $businessHoursService;
    private $stateService;
    private $nlpService;
    private $menuService;
    
    public function __construct()
    {
        $this->db = Database::getInstance();
        $this->whatsappService = new WhatsAppService();
        $this->businessHoursService = new BusinessHoursService();
        $this->stateService = new ConversationStateService();
        $this->nlpService = new NLPService();
        $this->menuService = new MenuService();
    }
    
    /**
     * Procesa un mensaje entrante
     * @param array $user
     * @param string $messageText
     * @param string $messageType
     * @param string|null $mediaId
     */
    public function processMessage(array $user, string $messageText, string $messageType = 'text', ?string $mediaId = null): void
    {
        $phoneNumber = $user['phone_number'];
        
        try {
            // 1. Verificar si el usuario está con un asesor
            if ($this->stateService->isWaitingForAdvisor($phoneNumber)) {
                // No procesar automáticamente, está con asesor humano
                $this->logMessage($phoneNumber, "Usuario con asesor, mensaje no procesado automáticamente");
                return;
            }
            
            // 2. Verificar horario de atención
            if (!$this->businessHoursService->isBusinessHours()) {
                if ($this->businessHoursService->shouldShowOutOfHoursMessage($phoneNumber)) {
                    $message = $this->businessHoursService->getMessage();
                    $this->sendMessage($phoneNumber, $message);
                }
                return;
            }
            
            // 3. Verificar si el estado ha expirado
            if ($this->stateService->isStateExpired($phoneNumber)) {
                $this->stateService->clearState($phoneNumber);
            }
            
            // 4. Procesar según el tipo de mensaje
            if ($messageType !== 'text' && $mediaId) {
                $this->processMediaMessage($user, $messageType, $mediaId, $messageText);
                return;
            }
            
            // 5. Verificar si es el primer mensaje (bienvenida)
            if ($this->isFirstMessage($phoneNumber)) {
                $this->sendWelcomeMessage($phoneNumber);
                return;
            }
            
            // 6. Procesar mensaje de texto con NLP
            $this->processTextMessage($user, $messageText);
            
        } catch (Exception $e) {
            error_log("Error en BotServiceLab->processMessage: " . $e->getMessage());
            $this->sendMessage($phoneNumber, "Lo siento, ocurrió un error. Por favor, intente de nuevo o escriba *ASESOR* para ayuda.");
        }
    }
    
    /**
     * Procesa un mensaje de texto
     * @param array $user
     * @param string $messageText
     */
    private function processTextMessage(array $user, string $messageText): void
    {
        $phoneNumber = $user['phone_number'];
        
        // Procesar con NLP para encontrar intención
        $response = $this->nlpService->processMessage($messageText, $phoneNumber);
        
        if (!$response) {
            // No se encontró respuesta, enviar mensaje por defecto
            $this->sendMessage($phoneNumber, "❓ No he comprendido su mensaje.\n\nEscriba *MENÚ* para ver las opciones disponibles.");
            return;
        }
        
        // Incrementar intentos si no entendió
        if ($response['type'] === 'default') {
            $attempts = $this->stateService->incrementAttempts($phoneNumber);
            
            // Si son 3 intentos fallidos, transferir a asesor
            if ($attempts >= 3) {
                $this->transferToAdvisor($phoneNumber, 'No se pudo entender el mensaje después de 3 intentos');
                return;
            }
        } else {
            // Resetear intentos si entendió correctamente
            $this->stateService->resetAttempts($phoneNumber);
        }
        
        // Detectar frustración
        if ($this->nlpService->detectsFrustration($messageText)) {
            $this->transferToAdvisor($phoneNumber, 'Frustración detectada');
            return;
        }
        
        // Ejecutar acción según el tipo de respuesta
        $this->executeResponse($phoneNumber, $response);
    }
    
    /**
     * Ejecuta la acción según el tipo de respuesta
     * @param string $phoneNumber
     * @param array $response
     */
    private function executeResponse(string $phoneNumber, array $response): void
    {
        switch ($response['type']) {
            case 'text':
                $this->sendMessage($phoneNumber, $response['message']);
                break;
                
            case 'menu':
                $menuId = $response['menu_id'] ?? null;
                if ($menuId) {
                    $this->sendMenu($phoneNumber, $menuId);
                }
                break;
                
            case 'template':
                $templateName = $response['template_name'] ?? null;
                if ($templateName) {
                    $this->sendTemplate($phoneNumber, $templateName);
                }
                break;
                
            case 'transfer':
                $message = $response['message'] ?? 'Conectando con asesor...';
                $this->transferToAdvisor($phoneNumber, 'Solicitud del usuario', $message);
                break;
        }
    }
    
    /**
     * Procesa mensajes multimedia
     * @param array $user
     * @param string $messageType
     * @param string $mediaId
     * @param string $caption
     */
    private function processMediaMessage(array $user, string $messageType, string $mediaId, string $caption = ''): void
    {
        $phoneNumber = $user['phone_number'];
        $currentState = $this->stateService->getState($phoneNumber);
        
        // Si está esperando foto de orden médica o documento
        if ($currentState && in_array($currentState['state'], [
            ConversationStateService::STATE_AWAITING_ORDER_PHOTO,
            ConversationStateService::STATE_AWAITING_ID_PHOTO
        ])) {
            if ($messageType === 'image' || $messageType === 'document') {
                $this->handleUploadedDocument($user, $mediaId, $currentState['state']);
            } else {
                $this->sendMessage($phoneNumber, "Por favor, envíe una imagen o documento PDF.");
            }
            return;
        }
        
        // Si no está esperando media, informar
        $this->sendMessage($phoneNumber, "He recibido su archivo. ¿En qué puedo ayudarle?\n\nEscriba *MENÚ* para ver las opciones.");
    }
    
    /**
     * Maneja documentos subidos (órdenes médicas, ID, etc.)
     * @param array $user
     * @param string $mediaId
     * @param string $expectedType
     */
    private function handleUploadedDocument(array $user, string $mediaId, string $expectedType): void
    {
        $phoneNumber = $user['phone_number'];
        
        // Validar que mediaId no esté vacío antes de intentar descargar
        if (empty($mediaId) || trim($mediaId) === '') {
            error_log("BotServiceLab: mediaId vacío recibido para usuario {$phoneNumber}");
            $this->sendMessage($phoneNumber, "❌ No se recibió correctamente el archivo. Por favor, envíelo nuevamente.");
            return;
        }
        
        try {
            // Descargar el archivo desde WhatsApp
            $mediaUrl = $this->whatsappService->downloadMedia($mediaId);
            
            if (!$mediaUrl) {
                $this->sendMessage($phoneNumber, "❌ No pude descargar el archivo. Por favor, intente de nuevo.");
                return;
            }
            
            // Guardar información del documento
            if ($expectedType === ConversationStateService::STATE_AWAITING_ORDER_PHOTO) {
                $this->stateService->updateStateData($phoneNumber, [
                    'order_photo_url' => $mediaUrl,
                    'order_received_at' => date('Y-m-d H:i:s')
                ]);
                
                $this->sendMessage($phoneNumber, "✅ Orden médica recibida.\n\nAhora necesito los datos del paciente:\n\n✓ Nombre completo:\n✓ Número de documento:\n✓ Dirección completa:\n✓ Número de celular:\n✓ Correo electrónico:\n✓ ¿Es particular o por seguro?\n\nEnvíelos en UN SOLO mensaje.");
                
                $this->stateService->setState($phoneNumber, ConversationStateService::STATE_AWAITING_PATIENT_DATA);
                
            } elseif ($expectedType === ConversationStateService::STATE_AWAITING_ID_PHOTO) {
                $this->stateService->updateStateData($phoneNumber, [
                    'id_photo_url' => $mediaUrl,
                    'id_received_at' => date('Y-m-d H:i:s')
                ]);
                
                $this->sendMessage($phoneNumber, "✅ Documento de identidad recibido.\n\nProcesando su solicitud...");
                
                // Aquí iría lógica adicional según el flujo
                $this->stateService->setState($phoneNumber, ConversationStateService::STATE_VALIDATING_DATA);
            }
            
        } catch (Exception $e) {
            error_log("Error downloading media: " . $e->getMessage());
                $this->sendMessage($phoneNumber, "❌ Hubo un problema al procesar el archivo. Por favor, intente nuevamente.");
        }
    }
    
    /**
     * Envía el mensaje de bienvenida
     * @param string $phoneNumber
     */
    private function sendWelcomeMessage(string $phoneNumber): void
    {
        $message = $this->businessHoursService->getMessage();
        $this->sendMessage($phoneNumber, $message);
        
        // Si está en horario, enviar menú principal
        if ($this->businessHoursService->isBusinessHours()) {
            $mainMenuId = $this->menuService->getMainMenuId();
            if ($mainMenuId) {
                $this->stateService->setCurrentMenu($phoneNumber, $mainMenuId);
            }
        }
    }
    
    /**
     * Envía un menú
     * @param string $phoneNumber
     * @param int $menuId
     */
    private function sendMenu(string $phoneNumber, int $menuId): void
    {
        $menu = $this->menuService->getMenu($menuId);
        
        if (!$menu) {
            $this->sendMessage($phoneNumber, "❌ Menú no encontrado.");
            return;
        }
        
        $this->sendMessage($phoneNumber, $menu['welcome_message']);
        $this->stateService->setCurrentMenu($phoneNumber, $menuId);
    }
    
    /**
     * Transfiere la conversación a un asesor
     * @param string $phoneNumber
     * @param string $reason
     * @param string $message
     */
    private function transferToAdvisor(string $phoneNumber, string $reason, string $message = null): void
    {
        // Enviar mensaje al usuario
        $userMessage = $message ?? '👤 SOLICITUD DE ASESOR\n\nUn momento por favor, estamos conectando tu consulta con uno de nuestros asesores.\n\nEn breve te atenderemos.\n\n*Tu consulta es importante para nosotros* 💙';
        
        $this->sendMessage($phoneNumber, $userMessage);
        
        // Marcar en el estado
        $this->stateService->markForTransfer($phoneNumber, $reason);
        
        // Aquí se podría notificar al equipo de asesores
        // Por ejemplo, enviar notificación, email, etc.
        $this->logMessage($phoneNumber, "Transferido a asesor: $reason");
    }
    
    /**
     * Envía una plantilla de WhatsApp
     * @param string $phoneNumber
     * @param string $templateName
     * @param array $parameters
     */
    private function sendTemplate(string $phoneNumber, string $templateName, array $parameters = []): void
    {
        try {
            $result = $this->whatsappService->sendTemplateMessage($phoneNumber, $templateName, $parameters);
            
            if (!$result) {
                $this->sendMessage($phoneNumber, "❌ No pude enviar la plantilla. Por favor, contacte a un asesor.");
            }
        } catch (Exception $e) {
            error_log("Error sending template: " . $e->getMessage());
            $this->sendMessage($phoneNumber, "❌ Error al enviar plantilla.");
        }
    }
    
    /**
     * Envía un mensaje de texto
     * @param string $phoneNumber
     * @param string $message
     * @return bool
     */
    private function sendMessage(string $phoneNumber, string $message): bool
    {
        try {
            $result = $this->whatsappService->sendTextMessage($phoneNumber, $message);
            
            if ($result) {
                // Guardar en BD
                $this->saveOutgoingMessage($phoneNumber, $message);
            }
            
            return $result;
        } catch (Exception $e) {
            error_log("Error sending message: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Guarda un mensaje saliente en la BD
     * @param string $phoneNumber
     * @param string $message
     */
    private function saveOutgoingMessage(string $phoneNumber, string $message): void
    {
        try {
            $stmt = $this->db->prepare("
                INSERT INTO conversations 
                (phone_number, message_type, message_text, direction, status, created_at)
                VALUES (?, 'text', ?, 'outgoing', 'sent', NOW())
            ");
            $stmt->execute([$phoneNumber, $message]);
        } catch (Exception $e) {
            error_log("Error saving outgoing message: " . $e->getMessage());
        }
    }
    
    /**
     * Verifica si es el primer mensaje del usuario
     * @param string $phoneNumber
     * @return bool
     */
    private function isFirstMessage(string $phoneNumber): bool
    {
        $stmt = $this->db->prepare("
            SELECT COUNT(*) as count 
            FROM conversations 
            WHERE phone_number = ? 
            AND direction = 'incoming'
        ");
        $stmt->execute([$phoneNumber]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        return $result && $result['count'] == 1; // Solo tiene un mensaje (el actual)
    }
    
    /**
     * Registra un log de la conversación
     * @param string $phoneNumber
     * @param string $message
     */
    private function logMessage(string $phoneNumber, string $message): void
    {
        error_log("[Bot - $phoneNumber] $message");
    }
}
