<?php
declare(strict_types=1);

namespace App\Framework\Http\Emitter;

use App\Framework\Http\Emitter\Emitter;
use App\Framework\Http\Response;
use App\Framework\Http\Responses\SseResponse;
use App\Framework\Http\SseStream;

final readonly class SseEmitter implements Emitter
{
    public function emit(Response $response): void
    {
        if($response instanceof SseResponse === false) {
            throw new \InvalidArgumentException('Response must be an instance of SseResponse');
        }

        // Stream-Objekt erstellen
        $stream = new SseStream();

        // Stream starten (sendet Header und initiale Events)
        $stream->start(
            $response->headers,
            $response->status,
            $response->initialEvents
        );

        // Streaming-Parameter
        $startTime = time();
        $lastHeartbeat = $startTime;
        $heartbeatInterval = $response->heartbeatInterval;
        $maxDuration = $response->maxDuration;
        $callback = $response->streamCallback;

        // Streaming-Loop
        while ($stream->isConnectionActive()) {
            // Prüfen, ob die maximale Dauer erreicht wurde
            if ($maxDuration > 0 && (time() - $startTime) >= $maxDuration) {
                $stream->sendJson(['message' => 'Maximum duration reached'], 'timeout');
                break;
            }

            // Callback ausführen, falls vorhanden
            if ($callback !== null) {
                try {
                    call_user_func($callback, $stream);
                } catch (\Throwable $e) {
                    // Fehler im Callback abfangen
                    $stream->sendJson([
                        'error' => $e->getMessage(),
                        'type' => get_class($e)
                    ], 'error');
                    break;
                }
            }

            // Heartbeat senden
            if (time() - $lastHeartbeat >= $heartbeatInterval) {
                $stream->sendHeartbeat();
                $lastHeartbeat = time();
            }

            // Kurze Pause, um CPU-Last zu reduzieren
            usleep(100_000); // 100ms
        }

        // Stream schließen
        $stream->close();
    }
}
