<?php

declare(strict_types=1);

namespace App\Framework\Http;

use App\Framework\Router\Result\SseEvent;

/**
 * Verwaltet den SSE-Stream für Server-Sent Events
 */
final class SseStream implements SseStreamInterface
{
    private bool $isStreaming = false;

    /**
     * Startet den SSE-Stream mit den angegebenen Headern und initialem Status
     *
     * @param Headers $headers Die zu sendenden HTTP-Header
     * @param Status $status Der zu sendende HTTP-Status-Code
     * @param array<SseEvent> $initialEvents Events, die beim Verbindungsaufbau gesendet werden
     */
    public function start(Headers $headers, Status $status, array $initialEvents = []): void
    {
        if ($this->isStreaming) {
            return;
        }

        // HTTP-Status senden
        http_response_code($status->value);

        // Headers senden
        foreach ($headers->all() as $name => $values) {
            // $values ist ein Array von Header-Werten
            $headerValue = is_array($values) ? implode(', ', $values) : (string) $values;
            header("{$name}: {$headerValue}");
        }

        // Output-Buffering deaktivieren
        while (ob_get_level() > 0) {
            ob_end_flush();
        }

        // Streamen aktivieren
        $this->isStreaming = true;

        // Initiale Events senden
        foreach ($initialEvents as $event) {
            $this->sendEvent($event);
        }
    }

    /**
     * Sendet ein einzelnes Event
     */
    public function sendEvent(SseEvent $event): void
    {
        if (! $this->isStreaming) {
            throw new \RuntimeException('Stream muss zuerst mit start() initialisiert werden');
        }

        echo $event->format() . "\n";
        $this->flush();
    }

    /**
     * Sendet ein Event mit den angegebenen Parametern
     */
    public function send(string $data, ?string $event = null, ?string $id = null, ?int $retry = null): void
    {
        $this->sendEvent(new SseEvent($data, $event, $id, $retry));
    }

    /**
     * Sendet JSON-Daten als Event
     */
    public function sendJson(array $data, ?string $event = null, ?string $id = null): void
    {
        $this->send(json_encode($data), $event, $id);
    }

    /**
     * Sendet ein Heartbeat-Event
     */
    public function sendHeartbeat(?string $id = null): void
    {
        $this->sendJson(['timestamp' => time()], 'heartbeat', $id);
    }

    /**
     * Sendet ein Retry-Intervall
     */
    public function sendRetry(int $milliseconds): void
    {
        $this->send('', null, null, $milliseconds);
    }

    /**
     * Schließt die Verbindung mit einem abschließenden Event
     */
    public function close(?string $message = null): void
    {
        if (! $this->isStreaming) {
            return;
        }

        $data = $message ?? 'Connection closed';
        $this->send($data, 'close');

        $this->isStreaming = false;
    }

    /**
     * Leert den Ausgabepuffer und sendet Daten zum Client
     */
    private function flush(): void
    {
        if (ob_get_level() > 0) {
            ob_flush();
        }
        flush();
    }

    /**
     * Prüft, ob die Client-Verbindung noch aktiv ist
     */
    public function isConnectionActive(): bool
    {
        return ! connection_aborted();
    }

    /**
     * Gibt zurück, ob der Stream aktiv ist
     */
    public function isActive(): bool
    {
        return $this->isStreaming;
    }
}
