#!/usr/bin/env php
<?php
/**
 * Simple WebSocket Server para Monitoreo en Tiempo Real
 * Sin dependencias externas - implementación básica
 */

// Cargar configuración directamente
function loadConfig() {
    $configFile = '/etc/centralita-tarot/config.env';
    if (!file_exists($configFile)) {
        die("Error: Archivo de configuración no encontrado: $configFile\n");
    }
    
    $config = parse_ini_file($configFile);
    if ($config === false) {
        die("Error: No se pudo parsear el archivo de configuración\n");
    }
    
    return $config;
}

class SimpleMonitorServer {
    protected $master;
    protected $sockets = [];
    protected $clients = [];
    protected $config;
    protected $db;
    protected $lastData = [];
    protected $lastCheck = 0;
    
    public function __construct($address = '0.0.0.0', $port = 8080) {
        $this->config = loadConfig();
        $this->connectDatabase();
        
        // Create WebSocket server
        $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1);
        socket_bind($this->master, $address, $port);
        socket_listen($this->master);
        
        $this->sockets[] = $this->master;
        
        echo "WebSocket Monitor Server started on $address:$port\n";
    }
    
    protected function connectDatabase() {
        $this->db = new mysqli(
            $this->config['DB_HOST'],
            $this->config['DB_USER'],
            $this->config['DB_PASSWORD'],
            $this->config['DB_NAME_ASTERISK']
        );
        
        if ($this->db->connect_error) {
            die("Database connection failed: " . $this->db->connect_error);
        }
        
        $this->db->set_charset("utf8mb4");
        echo "Database connected successfully\n";
    }
    
    public function run() {
        while (true) {
            // Check for database updates every 500ms
            if (microtime(true) - $this->lastCheck > 0.5) {
                $this->checkDatabaseUpdates();
                $this->lastCheck = microtime(true);
            }
            
            // Handle socket connections
            $read = $this->sockets;
            $write = null;
            $except = null;
            
            // Non-blocking select with 100ms timeout
            if (socket_select($read, $write, $except, 0, 100000) < 1) {
                continue;
            }
            
            // Handle new connections
            if (in_array($this->master, $read)) {
                $client = socket_accept($this->master);
                if ($client) {
                    $this->sockets[] = $client;
                    $clientId = spl_object_id($client);
                    $this->clients[$clientId] = [
                        'socket' => $client,
                        'handshake' => false,
                        'authenticated' => false
                    ];
                    echo "New client connected (ID: $clientId)\n";
                }
                
                $key = array_search($this->master, $read);
                unset($read[$key]);
            }
            
            // Handle client messages
            foreach ($read as $socket) {
                $data = @socket_recv($socket, $buffer, 2048, 0);
                
                if ($data === false || $data === 0) {
                    // Client disconnected
                    $this->disconnect($socket);
                    continue;
                }
                
                $socketId = spl_object_id($socket);
                
                if (!isset($this->clients[$socketId]) || !$this->clients[$socketId]['handshake']) {
                    // Perform WebSocket handshake
                    if ($this->doHandshake($buffer, $socket)) {
                        $this->clients[$socketId]['handshake'] = true;
                        
                        // Send initial data
                        $this->sendToClient($socket, [
                            'type' => 'initial',
                            'data' => $this->getCurrentData()
                        ]);
                    }
                } else {
                    // Handle WebSocket message
                    $message = $this->unmask($buffer);
                    $this->processMessage($socket, $message);
                }
            }
        }
    }
    
    protected function checkDatabaseUpdates() {
        $currentData = $this->getCurrentData();
        
        if ($this->hasChanges($currentData)) {
            $this->lastData = $currentData;
            $this->broadcastUpdate($currentData);
        }
    }
    
    protected function getCurrentData() {
        $data = [
            'timestamp' => date('Y-m-d H:i:s'),
            'unified_calls' => [],
            'agents' => [],
            'tarotistas' => [],
            'summary' => []
        ];
        
        // Get unified calls data
        $sql = "SELECT 
                ac.uniqueid,
                ac.caller_id,
                ac.destination,
                ac.start_time,
                ac.status,
                TIMESTAMPDIFF(SECOND, ac.start_time, NOW()) as duration_seconds,
                
                -- Coordinator info
                amc.extension as coordinator_ext,
                amc.coordinator_name,
                
                -- Agent info
                aac.agent_user,
                aac.agent_name,
                
                -- Determine who is attending
                CASE 
                    WHEN amc.uniqueid IS NOT NULL THEN 'coordinator'
                    WHEN aac.uniqueid IS NOT NULL THEN 'agent'
                    ELSE 'none'
                END as attended_by_type,
                
                CASE 
                    WHEN amc.uniqueid IS NOT NULL THEN CONCAT(amc.coordinator_name, ' (Ext: ', amc.extension, ')')
                    WHEN aac.uniqueid IS NOT NULL THEN CONCAT(aac.agent_name, ' (', aac.agent_user, ')')
                    ELSE 'Sin asignar'
                END as attended_by
                
            FROM active_calls ac
            LEFT JOIN active_manager_calls amc ON ac.uniqueid = amc.uniqueid
            LEFT JOIN active_agent_calls aac ON ac.uniqueid = aac.uniqueid
            WHERE ac.status IN ('ringing', 'answered')
            ORDER BY ac.start_time DESC";
            
        $result = $this->db->query($sql);
        if ($result) {
            while ($row = $result->fetch_assoc()) {
                // Adjust status based on assignment
                if ($row['status'] == 'ringing' && $row['attended_by_type'] == 'none') {
                    $row['status'] = 'waiting';
                } elseif ($row['attended_by_type'] != 'none') {
                    $row['status'] = 'active';
                }
                $data['unified_calls'][] = $row;
            }
            $result->free();
        }
        
        // Get agents status
        $sql = "SELECT 
                a.usuario,
                a.nombre,
                a.estado_login,
                a.estado_ocupacion,
                a.dnd_status,
                aac.uniqueid as current_call
            FROM agentes a
            LEFT JOIN active_agent_calls aac ON a.usuario = aac.agent_user 
                AND aac.status IN ('ringing', 'active')
            WHERE a.estado_login = 1
            ORDER BY a.usuario";
            
        $result = $this->db->query($sql);
        if ($result) {
            while ($row = $result->fetch_assoc()) {
                $data['agents'][] = $row;
            }
            $result->free();
        }
        
        // Get summary statistics
        $data['summary'] = $this->getSummaryStats();
        
        return $data;
    }
    
    protected function getSummaryStats() {
        $stats = [
            'total_active' => 0,
            'waiting_calls' => 0,
            'agent_calls' => 0,
            'coordinator_calls' => 0,
            'agents_logged' => 0,
            'agents_available' => 0,
            'agents_busy' => 0,
            'agents_dnd' => 0
        ];
        
        // Count active calls by type
        $sql = "SELECT 
                COUNT(*) as total,
                SUM(CASE WHEN ac.status = 'ringing' AND amc.uniqueid IS NULL AND aac.uniqueid IS NULL THEN 1 ELSE 0 END) as waiting,
                SUM(CASE WHEN aac.uniqueid IS NOT NULL THEN 1 ELSE 0 END) as agent_calls,
                SUM(CASE WHEN amc.uniqueid IS NOT NULL THEN 1 ELSE 0 END) as coordinator_calls
            FROM active_calls ac
            LEFT JOIN active_manager_calls amc ON ac.uniqueid = amc.uniqueid
            LEFT JOIN active_agent_calls aac ON ac.uniqueid = aac.uniqueid
            WHERE ac.status IN ('ringing', 'answered')";
            
        $result = $this->db->query($sql);
        if ($result) {
            $row = $result->fetch_assoc();
            $stats['total_active'] = $row['total'];
            $stats['waiting_calls'] = $row['waiting'];
            $stats['agent_calls'] = $row['agent_calls'];
            $stats['coordinator_calls'] = $row['coordinator_calls'];
        }
        
        // Count agent states
        $sql = "SELECT 
                COUNT(CASE WHEN estado_login = 1 THEN 1 END) as logged,
                COUNT(CASE WHEN estado_login = 1 AND estado_ocupacion = 0 AND dnd_status = 0 THEN 1 END) as available,
                COUNT(CASE WHEN estado_login = 1 AND estado_ocupacion = 1 THEN 1 END) as busy,
                COUNT(CASE WHEN estado_login = 1 AND dnd_status = 1 THEN 1 END) as dnd
                FROM agentes";
        $result = $this->db->query($sql);
        if ($result) {
            $row = $result->fetch_assoc();
            $stats['agents_logged'] = $row['logged'];
            $stats['agents_available'] = $row['available'];
            $stats['agents_busy'] = $row['busy'];
            $stats['agents_dnd'] = $row['dnd'];
        }
        
        return $stats;
    }
    
    protected function hasChanges($currentData) {
        return json_encode($currentData) !== json_encode($this->lastData);
    }
    
    protected function broadcastUpdate($data) {
        $message = [
            'type' => 'update',
            'data' => $data
        ];
        
        $count = 0;
        foreach ($this->clients as $client) {
            if ($client['handshake'] && $client['authenticated']) {
                $this->sendToClient($client['socket'], $message);
                $count++;
            }
        }
        
        if ($count > 0) {
            echo "Broadcasted update to $count clients\n";
        }
    }
    
    protected function processMessage($socket, $message) {
        try {
            $data = json_decode($message, true);
            if (!$data) return;
            
            $socketId = spl_object_id($socket);
            
            switch ($data['type']) {
                case 'auth':
                    // Simple auth - in production, validate session token
                    if (!empty($data['token'])) {
                        $this->clients[$socketId]['authenticated'] = true;
                        $this->sendToClient($socket, ['type' => 'auth_success']);
                        
                        // Send current data after auth
                        $this->sendToClient($socket, [
                            'type' => 'initial',
                            'data' => $this->getCurrentData()
                        ]);
                    } else {
                        $this->sendToClient($socket, ['type' => 'auth_failed']);
                        $this->disconnect($socket);
                    }
                    break;
                    
                case 'ping':
                    $this->sendToClient($socket, ['type' => 'pong']);
                    break;
                    
                case 'refresh':
                    $this->sendToClient($socket, [
                        'type' => 'update',
                        'data' => $this->getCurrentData()
                    ]);
                    break;
            }
        } catch (Exception $e) {
            echo "Error processing message: " . $e->getMessage() . "\n";
        }
    }
    
    protected function sendToClient($socket, $data) {
        $message = json_encode($data);
        $message = $this->mask($message);
        @socket_write($socket, $message, strlen($message));
    }
    
    protected function doHandshake($buffer, $socket) {
        list($resource, $headers) = $this->parseHeaders($buffer);
        
        if (!isset($headers['Sec-WebSocket-Key'])) {
            return false;
        }
        
        $key = $headers['Sec-WebSocket-Key'];
        $acceptKey = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
        
        $response = "HTTP/1.1 101 Switching Protocols\r\n";
        $response .= "Upgrade: websocket\r\n";
        $response .= "Connection: Upgrade\r\n";
        $response .= "Sec-WebSocket-Accept: $acceptKey\r\n\r\n";
        
        socket_write($socket, $response, strlen($response));
        
        return true;
    }
    
    protected function parseHeaders($buffer) {
        $lines = explode("\r\n", $buffer);
        $resource = null;
        $headers = [];
        
        foreach ($lines as $line) {
            if (preg_match('/^GET (.+) HTTP/', $line, $matches)) {
                $resource = $matches[1];
            } elseif (preg_match('/^(.+): (.+)$/', $line, $matches)) {
                $headers[$matches[1]] = $matches[2];
            }
        }
        
        return [$resource, $headers];
    }
    
    protected function unmask($payload) {
        $length = ord($payload[1]) & 127;
        
        if ($length == 126) {
            $masks = substr($payload, 4, 4);
            $data = substr($payload, 8);
        } elseif ($length == 127) {
            $masks = substr($payload, 10, 4);
            $data = substr($payload, 14);
        } else {
            $masks = substr($payload, 2, 4);
            $data = substr($payload, 6);
        }
        
        $text = '';
        for ($i = 0; $i < strlen($data); ++$i) {
            $text .= $data[$i] ^ $masks[$i % 4];
        }
        
        return $text;
    }
    
    protected function mask($text) {
        $b1 = 0x80 | (0x1 & 0x0f);
        $length = strlen($text);
        
        if ($length <= 125) {
            $header = pack('CC', $b1, $length);
        } elseif ($length > 125 && $length < 65536) {
            $header = pack('CCn', $b1, 126, $length);
        } else {
            $header = pack('CCNN', $b1, 127, $length);
        }
        
        return $header . $text;
    }
    
    protected function disconnect($socket) {
        $socketId = spl_object_id($socket);
        
        if (isset($this->clients[$socketId])) {
            unset($this->clients[$socketId]);
        }
        
        $key = array_search($socket, $this->sockets);
        if ($key !== false) {
            unset($this->sockets[$key]);
        }
        
        socket_close($socket);
        echo "Client disconnected\n";
    }
}

// Run the server
$server = new SimpleMonitorServer('0.0.0.0', 8080);
$server->run();