<?php
/**
 * WebSocket Server for Real-time Notifications
 * 
 * This script runs as a daemon process to handle WebSocket connections
 * and deliver real-time notifications to connected clients.
 * 
 * Usage: php websocket_server.php
 * 
 * Dependencies:
 * - Requires Ratchet library (Install via Composer)
 */

require_once '../../vendor/autoload.php';
require_once '../../config/database.php';

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

/**
 * Notification WebSocket Handler
 */
class NotificationServer implements MessageComponentInterface {
    protected $clients;
    protected $userConnections = [];
    protected $db;
    protected $debug = true;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
        $this->db = new Database();
        $this->log("Notification WebSocket Server Started");
    }

    /**
     * When a new WebSocket connection is established
     */
    public function onOpen(ConnectionInterface $conn) {
        $this->clients->attach($conn);
        $this->log("New connection! ({$conn->resourceId})");
    }

    /**
     * When a WebSocket message is received
     */
    public function onMessage(ConnectionInterface $from, $msg) {
        $data = json_decode($msg, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            $this->log("Invalid JSON received");
            return;
        }
        
        $this->log("Message received: " . print_r($data, true));
        
        // Handle authentication
        if (isset($data['action']) && $data['action'] === 'auth') {
            if (isset($data['user_id']) && isset($data['user_type']) && isset($data['auth_token'])) {
                // Validate token
                if ($this->validateAuthToken($data['user_id'], $data['user_type'], $data['auth_token'])) {
                    // Store connection
                    $userId = $data['user_id'];
                    $userType = $data['user_type'];
                    $userKey = $userType . '_' . $userId;
                    
                    if (!isset($this->userConnections[$userKey])) {
                        $this->userConnections[$userKey] = [];
                    }
                    
                    $this->userConnections[$userKey][] = $from;
                    $from->userId = $userId;
                    $from->userType = $userType;
                    
                    $this->log("User authenticated: $userType $userId");
                    
                    // Send confirmation
                    $from->send(json_encode([
                        'type' => 'auth',
                        'status' => 'success',
                        'message' => 'Authentication successful'
                    ]));
                    
                    // Send any pending notifications
                    $this->sendPendingNotifications($userId, $userType, $from);
                } else {
                    $from->send(json_encode([
                        'type' => 'auth',
                        'status' => 'error',
                        'message' => 'Authentication failed'
                    ]));
                }
            }
        }
        
        // Handle manual notification requests
        if (isset($data['action']) && $data['action'] === 'get_notifications') {
            if (isset($from->userId) && isset($from->userType)) {
                $this->sendPendingNotifications($from->userId, $from->userType, $from);
            }
        }
    }

    /**
     * When a WebSocket connection is closed
     */
    public function onClose(ConnectionInterface $conn) {
        // Remove connection from user list
        if (isset($conn->userId) && isset($conn->userType)) {
            $userKey = $conn->userType . '_' . $conn->userId;
            
            if (isset($this->userConnections[$userKey])) {
                $index = array_search($conn, $this->userConnections[$userKey]);
                if ($index !== false) {
                    unset($this->userConnections[$userKey][$index]);
                    $this->userConnections[$userKey] = array_values($this->userConnections[$userKey]);
                    
                    if (empty($this->userConnections[$userKey])) {
                        unset($this->userConnections[$userKey]);
                    }
                }
            }
        }
        
        // Detach from clients list
        $this->clients->detach($conn);
        $this->log("Connection {$conn->resourceId} has disconnected");
    }

    /**
     * Handle WebSocket errors
     */
    public function onError(ConnectionInterface $conn, \Exception $e) {
        $this->log("Error: {$e->getMessage()}");
        $conn->close();
    }

    /**
     * Validate user authentication token
     */
    protected function validateAuthToken($userId, $userType, $token) {
        try {
            $conn = $this->db->getConnection();
            
            if ($userType === 'admin') {
                $query = "SELECT id FROM admins WHERE id = :id AND status = 'active'";
            } else {
                $query = "SELECT id FROM members WHERE id = :id AND status = 'active'";
            }
            
            $stmt = $conn->prepare($query);
            $stmt->bindValue(':id', $userId);
            $stmt->execute();
            
            if ($stmt->rowCount() > 0) {
                // In a real-world scenario, we would validate the token against a stored value
                // For simplicity, we're just checking if the user exists
                return true;
            }
            
            return false;
        } catch (Exception $e) {
            $this->log("Auth validation error: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Send pending notifications to a user
     */
    protected function sendPendingNotifications($userId, $userType, $conn) {
        try {
            $pdo = $this->db->getConnection();
            
            // Get unread notifications
            $query = "SELECT 
                        n.id, n.type, n.title, n.message, n.priority, n.created_at,
                        CONCAT(a.first_name, ' ', a.last_name) as created_by_name,
                        n.related_entity_type, n.related_entity_id
                      FROM notifications n
                      JOIN notification_recipients nr ON n.id = nr.notification_id
                      LEFT JOIN admins a ON n.created_by = a.id
                      WHERE nr.user_id = :user_id 
                      AND nr.user_type = :user_type
                      AND nr.is_read = 0
                      ORDER BY n.created_at DESC
                      LIMIT 10";
            
            $stmt = $pdo->prepare($query);
            $stmt->bindValue(':user_id', $userId);
            $stmt->bindValue(':user_type', $userType);
            $stmt->execute();
            
            $notifications = $stmt->fetchAll(PDO::FETCH_ASSOC);
            
            if (!empty($notifications)) {
                $conn->send(json_encode([
                    'type' => 'notifications',
                    'data' => $notifications
                ]));
                
                $this->log("Sent " . count($notifications) . " notifications to $userType $userId");
            }
        } catch (Exception $e) {
            $this->log("Error sending notifications: " . $e->getMessage());
        }
    }

    /**
     * Broadcast a notification to specific users or all users
     */
    public function broadcastNotification($notification, $recipients = []) {
        // If recipients is empty, broadcast to all
        if (empty($recipients)) {
            foreach ($this->clients as $client) {
                if (isset($client->userId)) {
                    $client->send(json_encode([
                        'type' => 'notification',
                        'data' => $notification
                    ]));
                }
            }
            
            $this->log("Broadcast notification to all users");
            return;
        }
        
        // Send to specific recipients
        foreach ($recipients as $recipient) {
            $userKey = $recipient['user_type'] . '_' . $recipient['user_id'];
            
            if (isset($this->userConnections[$userKey])) {
                foreach ($this->userConnections[$userKey] as $conn) {
                    $conn->send(json_encode([
                        'type' => 'notification',
                        'data' => $notification
                    ]));
                }
                
                $this->log("Sent notification to $userKey");
            }
        }
    }

    /**
     * Log a message
     */
    protected function log($message) {
        if ($this->debug) {
            echo "[" . date('Y-m-d H:i:s') . "] $message\n";
        }
    }
}

// Start the WebSocket server
$server = IoServer::factory(
    new HttpServer(
        new WsServer(
            new NotificationServer()
        )
    ),
    8080 // WebSocket server port
);

echo "KSO Chandigarh Notification WebSocket Server running on port 8080\n";
$server->run();
