<?php

/**
 * Servicio de Gestión de Estados de Conversación
 * Controla el flujo y contexto de las conversaciones
 */

class ConversationStateService
{
    private $db;
    
    // Estados posibles
    const STATE_INITIAL = 'initial';
    const STATE_MAIN_MENU = 'main_menu';
    const STATE_SUBMENU_EXAMS = 'submenu_exams';
    const STATE_REQUESTING_HOME_SERVICE = 'requesting_home_service';
    const STATE_AWAITING_ORDER_PHOTO = 'awaiting_order_photo';
    const STATE_AWAITING_PATIENT_DATA = 'awaiting_patient_data';
    const STATE_AWAITING_ID_PHOTO = 'awaiting_id_photo';
    const STATE_VALIDATING_DATA = 'validating_data';
    const STATE_QUOTATION = 'quotation';
    const STATE_AWAITING_CONFIRMATION = 'awaiting_confirmation';
    const STATE_SCHEDULED = 'scheduled';
    const STATE_REQUESTING_RESULTS_HELP = 'requesting_results_help';
    const STATE_WITH_ADVISOR = 'with_advisor';
    const STATE_COMPLETED = 'completed';
    
    public function __construct()
    {
        $this->db = Database::getInstance();
    }
    
    /**
     * Obtiene el estado actual de un usuario
     * @param string $phoneNumber
     * @return array|null
     */
    public function getState(string $phoneNumber): ?array
    {
        $state = $this->db->fetch(
            "SELECT * FROM user_states WHERE phone_number = :phone ORDER BY id DESC LIMIT 1",
            ['phone' => $phoneNumber]
        );
        
        if ($state && $state['state_data']) {
            $state['state_data'] = json_decode($state['state_data'], true);
        }
        
        return $state ?: null;
    }
    
    /**
     * Establece un nuevo estado para el usuario
     * @param string $phoneNumber
     * @param string $state
     * @param array $data Datos adicionales del estado
     * @return bool
     */
    public function setState(string $phoneNumber, string $state, array $data = []): bool
    {
        $sql = "INSERT INTO user_states (phone_number, state, state_data, created_at, updated_at)
            VALUES (:phone, :state, :state_data, NOW(), NOW())
            ON DUPLICATE KEY UPDATE 
                state = VALUES(state),
                state_data = VALUES(state_data),
                updated_at = NOW()";

        $stateDataJson = !empty($data) ? json_encode($data, JSON_UNESCAPED_UNICODE) : null;

        $this->db->query($sql, ['phone' => $phoneNumber, 'state' => $state, 'state_data' => $stateDataJson]);
        return true;
    }
    
    /**
     * Actualiza los datos del estado actual sin cambiar el estado
     * @param string $phoneNumber
     * @param array $data
     * @return bool
     */
    public function updateStateData(string $phoneNumber, array $data): bool
    {
        $currentState = $this->getState($phoneNumber);
        
        if (!$currentState) {
            return false;
        }
        
        $existingData = $currentState['state_data'] ?? [];
        $mergedData = array_merge($existingData, $data);
        
        $this->db->query(
            "UPDATE user_states SET state_data = :sd, updated_at = NOW() WHERE phone_number = :phone",
            ['sd' => json_encode($mergedData, JSON_UNESCAPED_UNICODE), 'phone' => $phoneNumber]
        );
        return true;
    }
    
    /**
     * Obtiene datos específicos del estado
     * @param string $phoneNumber
     * @param string $key
     * @return mixed
     */
    public function getStateData(string $phoneNumber, string $key)
    {
        $state = $this->getState($phoneNumber);
        
        if (!$state || !isset($state['state_data'])) {
            return null;
        }
        
        return $state['state_data'][$key] ?? null;
    }
    
    /**
     * Limpia el estado del usuario
     * @param string $phoneNumber
     * @return bool
     */
    public function clearState(string $phoneNumber): bool
    {
        try {
            $this->db->delete('user_states', 'phone_number = :phone', ['phone' => $phoneNumber]);
            return true;
        } catch (Exception $e) {
            error_log('[ConversationStateService] clearState failed: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Verifica si el usuario está en un flujo específico
     * @param string $phoneNumber
     * @param string $state
     * @return bool
     */
    public function isInState(string $phoneNumber, string $state): bool
    {
        $currentState = $this->getState($phoneNumber);
        return $currentState && $currentState['state'] === $state;
    }
    
    /**
     * Obtiene el contador de intentos
     * @param string $phoneNumber
     * @return int
     */
    public function getAttemptCount(string $phoneNumber): int
    {
        return (int)($this->getStateData($phoneNumber, 'attempt_count') ?? 0);
    }
    
    /**
     * Incrementa el contador de intentos
     * @param string $phoneNumber
     * @return int Nuevo número de intentos
     */
    public function incrementAttempts(string $phoneNumber): int
    {
        $attempts = $this->getAttemptCount($phoneNumber) + 1;
        $this->updateStateData($phoneNumber, ['attempt_count' => $attempts]);
        return $attempts;
    }
    
    /**
     * Resetea el contador de intentos
     * @param string $phoneNumber
     * @return bool
     */
    public function resetAttempts(string $phoneNumber): bool
    {
        return $this->updateStateData($phoneNumber, ['attempt_count' => 0]);
    }
    
    /**
     * Guarda datos temporales del agendamiento
     * @param string $phoneNumber
     * @param array $appointmentData
     * @return bool
     */
    public function saveAppointmentData(string $phoneNumber, array $appointmentData): bool
    {
        return $this->updateStateData($phoneNumber, [
            'appointment' => $appointmentData,
            'appointment_started_at' => date('Y-m-d H:i:s')
        ]);
    }
    
    /**
     * Obtiene datos del agendamiento en curso
     * @param string $phoneNumber
     * @return array|null
     */
    public function getAppointmentData(string $phoneNumber): ?array
    {
        return $this->getStateData($phoneNumber, 'appointment');
    }
    
    /**
     * Verifica si el estado ha expirado (más de 30 minutos de inactividad)
     * @param string $phoneNumber
     * @return bool
     */
    public function isStateExpired(string $phoneNumber): bool
    {
        $state = $this->getState($phoneNumber);
        
        if (!$state) {
            return false;
        }
        
        $updatedAt = strtotime($state['updated_at']);
        $now = time();
        $diffMinutes = ($now - $updatedAt) / 60;
        
        // Expirar después de 30 minutos
        return $diffMinutes > 30;
    }
    
    /**
     * Limpia estados expirados de todos los usuarios
     * @return int Número de estados eliminados
     */
    public function cleanExpiredStates(): int
    {
        $sql = "DELETE FROM user_states 
            WHERE updated_at < DATE_SUB(NOW(), INTERVAL 30 MINUTE)
            AND state NOT IN (?, ?)";

        return $this->db->execute($sql, [
            self::STATE_SCHEDULED,
            self::STATE_WITH_ADVISOR
        ]);
    }
    
    /**
     * Obtiene el menú actual del usuario
     * @param string $phoneNumber
     * @return int|null ID del menú
     */
    public function getCurrentMenuId(string $phoneNumber): ?int
    {
        return $this->getStateData($phoneNumber, 'current_menu_id');
    }
    
    /**
     * Establece el menú actual
     * @param string $phoneNumber
     * @param int $menuId
     * @return bool
     */
    public function setCurrentMenu(string $phoneNumber, ?int $menuId): bool
    {
        // Allow null to clear current_menu_id
        return $this->updateStateData($phoneNumber, ['current_menu_id' => $menuId]);
    }
    
    /**
     * Marca la conversación para transferir a asesor
     * @param string $phoneNumber
     * @param string $reason
     * @return bool
     */
    public function markForTransfer(string $phoneNumber, string $reason = ''): bool
    {
        return $this->setState($phoneNumber, self::STATE_WITH_ADVISOR, [
            'transfer_reason' => $reason,
            'transferred_at' => date('Y-m-d H:i:s')
        ]);
    }
    
    /**
     * Verifica si el usuario está esperando asesor
     * @param string $phoneNumber
     * @return bool
     */
    public function isWaitingForAdvisor(string $phoneNumber): bool
    {
        return $this->isInState($phoneNumber, self::STATE_WITH_ADVISOR);
    }
}
