<?php
/**
 * Gestor de estados de conversación en Redis
 * Mantiene estados temporales de chat para flujos conversacionales
 */

namespace WhatsApp\Queue;

use Predis\Client as RedisClient;

class ConversationState {
    private $redis;
    private $prefix = 'chat:state:';
    private $defaultTTL = 3600; // 1 hora
    
    public function __construct(RedisClient $redis = null) {
        $this->redis = $redis ?? new RedisClient([
            'scheme' => getenv('REDIS_SCHEME') ?: 'tcp',
            'host'   => getenv('REDIS_HOST') ?: '127.0.0.1',
            'port'   => getenv('REDIS_PORT') ?: 6379,
            'password' => getenv('REDIS_PASSWORD') ?: null,
            'database' => getenv('REDIS_DB') ?: 0,
        ]);
    }
    
    /**
     * Establecer estado de conversación
     * 
     * @param string $userId ID del usuario
     * @param string $state Estado actual (ej: 'waiting_name', 'selecting_option', 'uploading_document')
     * @param array $context Datos adicionales del contexto
     * @param int $ttl Tiempo de vida en segundos
     * @return bool
     */
    public function setState(string $userId, string $state, array $context = [], int $ttl = null): bool {
        try {
            $key = $this->prefix . $userId;
            $ttl = $ttl ?? $this->defaultTTL;
            
            $data = [
                'state' => $state,
                'context' => $context,
                'updated_at' => time()
            ];
            
            $this->redis->setex($key, $ttl, json_encode($data));
            
            return true;
        } catch (\Exception $e) {
            error_log("Failed to set conversation state: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Obtener estado actual de conversación
     * 
     * @param string $userId
     * @return array|null ['state' => string, 'context' => array, 'updated_at' => int]
     */
    public function getState(string $userId): ?array {
        try {
            $key = $this->prefix . $userId;
            $data = $this->redis->get($key);
            
            if (!$data) {
                return null;
            }
            
            return json_decode($data, true);
        } catch (\Exception $e) {
            error_log("Failed to get conversation state: " . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Actualizar contexto sin cambiar el estado
     * 
     * @param string $userId
     * @param array $context Datos a agregar/actualizar en el contexto
     * @return bool
     */
    public function updateContext(string $userId, array $context): bool {
        try {
            $current = $this->getState($userId);
            
            if (!$current) {
                return false;
            }
            
            $current['context'] = array_merge($current['context'] ?? [], $context);
            $current['updated_at'] = time();
            
            $key = $this->prefix . $userId;
            $ttl = $this->redis->ttl($key);
            
            // Mantener el TTL original
            if ($ttl > 0) {
                $this->redis->setex($key, $ttl, json_encode($current));
            } else {
                $this->redis->setex($key, $this->defaultTTL, json_encode($current));
            }
            
            return true;
        } catch (\Exception $e) {
            error_log("Failed to update conversation context: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Limpiar estado (finalizar conversación)
     * 
     * @param string $userId
     * @return bool
     */
    public function clearState(string $userId): bool {
        try {
            $key = $this->prefix . $userId;
            $this->redis->del([$key]);
            
            return true;
        } catch (\Exception $e) {
            error_log("Failed to clear conversation state: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Extender TTL del estado actual
     * 
     * @param string $userId
     * @param int $additionalSeconds Segundos adicionales
     * @return bool
     */
    public function extendTTL(string $userId, int $additionalSeconds): bool {
        try {
            $key = $this->prefix . $userId;
            $currentTTL = $this->redis->ttl($key);
            
            if ($currentTTL > 0) {
                $this->redis->expire($key, $currentTTL + $additionalSeconds);
                return true;
            }
            
            return false;
        } catch (\Exception $e) {
            error_log("Failed to extend conversation TTL: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Verificar si usuario está en un estado específico
     * 
     * @param string $userId
     * @param string $expectedState
     * @return bool
     */
    public function isInState(string $userId, string $expectedState): bool {
        $current = $this->getState($userId);
        return $current && $current['state'] === $expectedState;
    }
    
    /**
     * Obtener valor específico del contexto
     * 
     * @param string $userId
     * @param string $key
     * @param mixed $default
     * @return mixed
     */
    public function getContextValue(string $userId, string $key, $default = null) {
        $state = $this->getState($userId);
        
        if (!$state || !isset($state['context'][$key])) {
            return $default;
        }
        
        return $state['context'][$key];
    }
    
    /**
     * Almacenar datos temporales (para flujos multi-paso)
     * 
     * Ejemplo: Durante un proceso de registro que requiere nombre, email, teléfono
     * se van guardando los datos hasta completar el formulario
     * 
     * @param string $userId
     * @param string $key
     * @param mixed $value
     * @return bool
     */
    public function setTemporaryData(string $userId, string $key, $value): bool {
        try {
            $tempKey = $this->prefix . 'temp:' . $userId . ':' . $key;
            $this->redis->setex($tempKey, 1800, json_encode($value)); // 30 minutos
            
            return true;
        } catch (\Exception $e) {
            error_log("Failed to set temporary data: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Obtener datos temporales
     * 
     * @param string $userId
     * @param string $key
     * @return mixed|null
     */
    public function getTemporaryData(string $userId, string $key) {
        try {
            $tempKey = $this->prefix . 'temp:' . $userId . ':' . $key;
            $data = $this->redis->get($tempKey);
            
            if (!$data) {
                return null;
            }
            
            return json_decode($data, true);
        } catch (\Exception $e) {
            error_log("Failed to get temporary data: " . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Limpiar datos temporales
     * 
     * @param string $userId
     * @param string $key Si no se especifica, limpia todos los datos temp del usuario
     * @return bool
     */
    public function clearTemporaryData(string $userId, string $key = null): bool {
        try {
            if ($key) {
                $tempKey = $this->prefix . 'temp:' . $userId . ':' . $key;
                $this->redis->del([$tempKey]);
            } else {
                // Limpiar todos los datos temp del usuario
                $pattern = $this->prefix . 'temp:' . $userId . ':*';
                $keys = $this->redis->keys($pattern);
                
                if (!empty($keys)) {
                    $this->redis->del($keys);
                }
            }
            
            return true;
        } catch (\Exception $e) {
            error_log("Failed to clear temporary data: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Marcar usuario como "escribiendo..." (útil para UX)
     * 
     * @param string $userId
     * @param int $ttl Segundos (típicamente 3-5 segundos)
     * @return bool
     */
    public function setTyping(string $userId, int $ttl = 5): bool {
        try {
            $key = $this->prefix . 'typing:' . $userId;
            $this->redis->setex($key, $ttl, '1');
            
            return true;
        } catch (\Exception $e) {
            error_log("Failed to set typing indicator: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Verificar si usuario está escribiendo
     * 
     * @param string $userId
     * @return bool
     */
    public function isTyping(string $userId): bool {
        try {
            $key = $this->prefix . 'typing:' . $userId;
            return (bool) $this->redis->exists($key);
        } catch (\Exception $e) {
            return false;
        }
    }
    
    /**
     * Obtener estadísticas de estados activos
     * 
     * @return array
     */
    public function getActiveStates(): array {
        try {
            $pattern = $this->prefix . '*';
            $keys = $this->redis->keys($pattern);
            
            $states = [];
            foreach ($keys as $key) {
                // Excluir keys temporales y typing
                if (strpos($key, ':temp:') !== false || strpos($key, ':typing:') !== false) {
                    continue;
                }
                
                $data = $this->redis->get($key);
                if ($data) {
                    $decoded = json_decode($data, true);
                    $state = $decoded['state'] ?? 'unknown';
                    
                    if (!isset($states[$state])) {
                        $states[$state] = 0;
                    }
                    $states[$state]++;
                }
            }
            
            return $states;
        } catch (\Exception $e) {
            error_log("Failed to get active states: " . $e->getMessage());
            return [];
        }
    }
}
