<?php

declare(strict_types=1);

namespace App\Framework\Http\Emitter;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\Timer;
use App\Framework\Http\Response;
use App\Framework\Http\Responses\SseResponse;
use App\Framework\Http\SseStream;
use InvalidArgumentException;
use Throwable;

final readonly class SseEmitter implements Emitter
{
    public function __construct(
        private Timer $timer
    ) {
    }

    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() && $stream->isActive()) {
            // 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
            $this->timer->sleep(Duration::fromMilliseconds(100));
        }

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

        // PHP-Ausführung beenden, um zusätzliche Ausgabe zu verhindern
        exit();
    }
}
