<?php
/**
 * WebSocket Server for KSO Admin Panel
 * 
 * This server handles real-time notifications and updates for the KSO Admin Panel
 * 
 * Usage: php websocket_server.php
 */

namespace Services\Core\WebSocket;

use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;

require_once __DIR__ . '/../../../../vendor/autoload.php';

/**
 * WebSocket handler class
 */
class WebSocketHandler implements MessageComponentInterface {
    protected $clients;
    protected $adminClients = [];
    protected $memberClients = [];
    protected $connectionMap = [];
    
    /**
     * Constructor
     */
    public function __construct() {
        $this->clients = new \SplObjectStorage;
        echo "WebSocket server started at " . date('Y-m-d H:i:s') . "\n";
    }
    
    /**
     * Handle new connection
     * 
     * @param ConnectionInterface $conn Connection object
     */
    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection
        $this->clients->attach($conn);
        
        echo "New connection! ({$conn->resourceId})\n";
    }
    
    /**
     * Handle incoming messages
     * 
     * @param ConnectionInterface $from Connection that sent the message
     * @param string $msg The message
     */
    public function onMessage(ConnectionInterface $from, $msg) {
        $data = json_decode($msg, true);
        
        // Process the message based on its type
        if (isset($data['type'])) {
            switch ($data['type']) {
                // Authentication message
                case 'authenticate':
                    $this->handleAuthentication($from, $data);
                    break;
                
                // Notification message
                case 'notification':
                    $this->handleNotification($from, $data);
                    break;
                
                // Status update message
                case 'status':
                    $this->handleStatusUpdate($from, $data);
                    break;
                
                // Default handler for other message types
                default:
                    echo "Unknown message type: {$data['type']}\n";
                    break;
            }
        }
    }
    
    /**
     * Handle authentication messages
     * 
     * @param ConnectionInterface $conn Connection object
     * @param array $data Authentication data
     */
    protected function handleAuthentication($conn, $data) {
        if (isset($data['user_id']) && isset($data['user_type']) && isset($data['auth_token'])) {
            $userId = $data['user_id'];
            $userType = $data['user_type'];
            $authToken = $data['auth_token'];
            
            // Validate the token (simplified for example)
            if ($this->validateAuthToken($userId, $userType, $authToken)) {
                // Map the connection to user
                $this->connectionMap[$conn->resourceId] = [
                    'user_id' => $userId,
                    'user_type' => $userType
                ];
                
                // Add to the appropriate client list
                if ($userType === 'admin') {
                    if (!isset($this->adminClients[$userId])) {
                        $this->adminClients[$userId] = [];
                    }
                    $this->adminClients[$userId][] = $conn;
                } else if ($userType === 'member') {
                    if (!isset($this->memberClients[$userId])) {
                        $this->memberClients[$userId] = [];
                    }
                    $this->memberClients[$userId][] = $conn;
                }
                
                // Send success response
                $conn->send(json_encode([
                    'type' => 'auth_response',
                    'success' => true,
                    'message' => 'Successfully authenticated'
                ]));
                
                echo "User authenticated: $userType #$userId (Connection: {$conn->resourceId})\n";
            } else {
                // Authentication failed
                $conn->send(json_encode([
                    'type' => 'auth_response',
                    'success' => false,
                    'message' => 'Authentication failed'
                ]));
                
                echo "Authentication failed for: $userType #$userId (Connection: {$conn->resourceId})\n";
            }
        } else {
            // Missing authentication data
            $conn->send(json_encode([
                'type' => 'auth_response',
                'success' => false,
                'message' => 'Missing authentication data'
            ]));
            
            echo "Missing authentication data (Connection: {$conn->resourceId})\n";
        }
    }
    
    /**
     * Validate authentication token
     * 
     * @param int $userId User ID
     * @param string $userType User type ('admin' or 'member')
     * @param string $authToken Authentication token
     * @return bool True if valid, false otherwise
     */
    protected function validateAuthToken($userId, $userType, $authToken) {
        // TODO: Implement proper token validation with database check
        // This is a simplified example
        
        // For testing purposes, accept all tokens
        return true;
    }
    
    /**
     * Handle notification messages
     * 
     * @param ConnectionInterface $from Connection that sent the message
     * @param array $data Notification data
     */
    protected function handleNotification($from, $data) {
        // Ensure sender is authenticated
        if (!isset($this->connectionMap[$from->resourceId])) {
            $from->send(json_encode([
                'type' => 'error',
                'message' => 'You must be authenticated to send notifications'
            ]));
            return;
        }
        
        // Get sender info
        $sender = $this->connectionMap[$from->resourceId];
        
        // Check if notification data is valid
        if (!isset($data['data']) || !isset($data['recipients'])) {
            $from->send(json_encode([
                'type' => 'error',
                'message' => 'Invalid notification data'
            ]));
            return;
        }
        
        // Process notification
        $notification = $data['data'];
        $recipients = $data['recipients'];
        
        // Add sender info to notification
        $notification['sender'] = [
            'id' => $sender['user_id'],
            'type' => $sender['user_type']
        ];
        
        // Prepare message to send
        $message = json_encode([
            'type' => 'notification',
            'data' => $notification
        ]);
        
        // Send to specific recipients
        $this->sendToRecipients($message, $recipients);
        
        echo "Notification sent from {$sender['user_type']} #{$sender['user_id']} to " . count($recipients) . " recipients\n";
    }
    
    /**
     * Handle status update messages
     * 
     * @param ConnectionInterface $from Connection that sent the message
     * @param array $data Status data
     */
    protected function handleStatusUpdate($from, $data) {
        // Ensure sender is authenticated
        if (!isset($this->connectionMap[$from->resourceId])) {
            $from->send(json_encode([
                'type' => 'error',
                'message' => 'You must be authenticated to send status updates'
            ]));
            return;
        }
        
        // Get sender info
        $sender = $this->connectionMap[$from->resourceId];
        
        // Check if status data is valid
        if (!isset($data['status'])) {
            $from->send(json_encode([
                'type' => 'error',
                'message' => 'Invalid status data'
            ]));
            return;
        }
        
        // Process status update
        $status = $data['status'];
        
        // Prepare message to send
        $message = json_encode([
            'type' => 'status_update',
            'data' => [
                'user_id' => $sender['user_id'],
                'user_type' => $sender['user_type'],
                'status' => $status
            ]
        ]);
        
        // Send to all admin users
        $this->sendToAllAdmins($message);
        
        echo "Status update from {$sender['user_type']} #{$sender['user_id']}: $status\n";
    }
    
    /**
     * Send message to specific recipients
     * 
     * @param string $message Message to send
     * @param array $recipients Array of recipient objects with recipient_id and recipient_type
     */
    protected function sendToRecipients($message, $recipients) {
        foreach ($recipients as $recipient) {
            $recipientId = $recipient['recipient_id'];
            $recipientType = $recipient['recipient_type'];
            
            if ($recipientType === 'admin' && isset($this->adminClients[$recipientId])) {
                foreach ($this->adminClients[$recipientId] as $conn) {
                    $conn->send($message);
                }
            } else if ($recipientType === 'member' && isset($this->memberClients[$recipientId])) {
                foreach ($this->memberClients[$recipientId] as $conn) {
                    $conn->send($message);
                }
            }
        }
    }
    
    /**
     * Send message to all admin users
     * 
     * @param string $message Message to send
     */
    protected function sendToAllAdmins($message) {
        foreach ($this->adminClients as $adminId => $connections) {
            foreach ($connections as $conn) {
                $conn->send($message);
            }
        }
    }
    
    /**
     * Handle closed connection
     * 
     * @param ConnectionInterface $conn Connection that closed
     */
    public function onClose(ConnectionInterface $conn) {
        // Remove connection from clients list
        $this->clients->detach($conn);
        
        // Remove from connection map
        if (isset($this->connectionMap[$conn->resourceId])) {
            $userId = $this->connectionMap[$conn->resourceId]['user_id'];
            $userType = $this->connectionMap[$conn->resourceId]['user_type'];
            
            // Remove from appropriate client list
            if ($userType === 'admin' && isset($this->adminClients[$userId])) {
                foreach ($this->adminClients[$userId] as $key => $adminConn) {
                    if ($adminConn === $conn) {
                        unset($this->adminClients[$userId][$key]);
                        break;
                    }
                }
                
                // Remove empty arrays
                if (empty($this->adminClients[$userId])) {
                    unset($this->adminClients[$userId]);
                }
            } else if ($userType === 'member' && isset($this->memberClients[$userId])) {
                foreach ($this->memberClients[$userId] as $key => $memberConn) {
                    if ($memberConn === $conn) {
                        unset($this->memberClients[$userId][$key]);
                        break;
                    }
                }
                
                // Remove empty arrays
                if (empty($this->memberClients[$userId])) {
                    unset($this->memberClients[$userId]);
                }
            }
            
            // Remove from connection map
            unset($this->connectionMap[$conn->resourceId]);
            
            echo "Connection {$conn->resourceId} closed ($userType #$userId)\n";
        } else {
            echo "Connection {$conn->resourceId} closed (unauthenticated)\n";
        }
    }
    
    /**
     * Handle errors
     * 
     * @param ConnectionInterface $conn Connection with error
     * @param \Exception $e Exception
     */
    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "Error: {$e->getMessage()}\n";
        
        $conn->close();
    }
}

// Start the server
$handler = new WebSocketHandler();
$server = IoServer::factory(
    new HttpServer(
        new WsServer(
            $handler
        )
    ),
    8080
);

echo "WebSocket server listening on port 8080\n";
$server->run();
