<?php

declare(strict_types=1);

namespace App\Framework\Http;

/**
 * Repräsentiert eine aktive WebSocket-Verbindung
 */
final class WebSocketConnection implements WebSocketConnectionInterface
{
    private bool $isConnected = true;

    private array $attributes = [];

    public function __construct(
        private readonly string $id,
        private $socket
    ) {
    }

    /**
     * Sendet eine Text-Nachricht
     */
    public function send(string $message): bool
    {
        if (! $this->isConnected) {
            return false;
        }

        try {
            $frame = $this->createFrame($message, 0x1); // Text frame

            return fwrite($this->socket, $frame) !== false;
        } catch (\Throwable $e) {
            $this->disconnect();

            return false;
        }
    }

    /**
     * Sendet JSON-Daten
     */
    public function sendJson(array $data): bool
    {
        return $this->send(json_encode($data));
    }

    /**
     * Sendet Binärdaten
     */
    public function sendBinary(string $data): bool
    {
        if (! $this->isConnected) {
            return false;
        }

        try {
            $frame = $this->createFrame($data, 0x2); // Binary frame

            return fwrite($this->socket, $frame) !== false;
        } catch (\Throwable $e) {
            $this->disconnect();

            return false;
        }
    }

    /**
     * Sendet einen Ping
     */
    public function ping(string $data = ''): bool
    {
        if (! $this->isConnected) {
            return false;
        }

        try {
            $frame = $this->createFrame($data, 0x9); // Ping frame

            return fwrite($this->socket, $frame) !== false;
        } catch (\Throwable $e) {
            $this->disconnect();

            return false;
        }
    }

    /**
     * Schließt die Verbindung
     */
    public function close(int $code = 1000, string $reason = ''): void
    {
        if (! $this->isConnected) {
            return;
        }

        try {
            $payload = pack('n', $code) . $reason;
            $frame = $this->createFrame($payload, 0x8); // Close frame
            fwrite($this->socket, $frame);
        } catch (\Throwable $e) {
            // Fehler beim Senden des Close-Frames ignorieren
        }

        $this->disconnect();
    }

    /**
     * Trennt die Verbindung
     */
    public function disconnect(): void
    {
        $this->isConnected = false;
        if (is_resource($this->socket)) {
            fclose($this->socket);
        }
    }

    /**
     * Erstellt einen WebSocket-Frame
     */
    private function createFrame(string $payload, int $opcode): string
    {
        $payloadLength = strlen($payload);
        $frame = '';

        // Erste Byte: FIN (1) + RSV (000) + Opcode (4 bits)
        $frame .= chr(0x80 | $opcode);

        // Payload-Länge
        if ($payloadLength < 126) {
            $frame .= chr($payloadLength);
        } elseif ($payloadLength < 65536) {
            $frame .= chr(126) . pack('n', $payloadLength);
        } else {
            $frame .= chr(127) . pack('J', $payloadLength);
        }

        return $frame . $payload;
    }

    /**
     * Setzt ein Attribut für diese Verbindung
     */
    public function setAttribute(string $key, mixed $value): void
    {
        $this->attributes[$key] = $value;
    }

    /**
     * Gibt ein Attribut zurück
     */
    public function getAttribute(string $key, mixed $default = null): mixed
    {
        return $this->attributes[$key] ?? $default;
    }

    /**
     * Prüft, ob die Verbindung aktiv ist
     */
    public function isConnected(): bool
    {
        return $this->isConnected;
    }

    /**
     * Gibt die Verbindungs-ID zurück
     */
    public function getId(): string
    {
        return $this->id;
    }
}
