<?php

declare(strict_types=1);

namespace App\Framework\Deployment\Pipeline\Http;

use App\Framework\Attributes\Route;
use App\Framework\Deployment\Pipeline\Services\PipelineStatusStore;
use App\Framework\Deployment\Pipeline\ValueObjects\PipelineId;
use App\Framework\Http\Headers;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\Method;
use App\Framework\Http\Results\JsonResult;
use App\Framework\Http\SseStream;
use App\Framework\Http\Status;
use App\Framework\Logging\Logger;

/**
 * Lightweight SSE controller for pipeline status streaming
 *
 * Provides real-time pipeline execution updates without requiring
 * a full LiveComponent. Simply opens an SSE connection and streams
 * events as the pipeline executes.
 *
 * Usage from frontend:
 * ```javascript
 * const eventSource = new EventSource('/api/pipeline/stream/{pipelineId}');
 * eventSource.addEventListener('pipeline.status', (event) => {
 *     const data = JSON.parse(event.data);
 *     // Update UI with pipeline progress
 * });
 * ```
 */
final readonly class PipelineEventStreamController
{
    public function __construct(
        private PipelineStatusStore $statusStore,
        private SseStream $sseStream,
        private Logger $logger
    ) {}

    /**
     * Stream pipeline execution events via SSE
     */
    #[Route(path: '/api/pipeline/stream/{pipelineId}', method: Method::GET)]
    public function streamPipelineEvents(string $pipelineId, HttpRequest $request): void
    {
        $pipelineId = PipelineId::fromString($pipelineId);

        // Verify pipeline exists
        if (!$this->statusStore->exists($pipelineId)) {
            http_response_code(404);
            echo json_encode(['error' => 'Pipeline not found']);
            return;
        }

        // Get initial state
        $initialState = $this->statusStore->getState($pipelineId);

        // Start SSE stream
        $headers = Headers::empty()
            ->with('Content-Type', 'text/event-stream')
            ->with('Cache-Control', 'no-cache')
            ->with('Connection', 'keep-alive')
            ->with('X-Accel-Buffering', 'no'); // Disable nginx buffering

        $this->sseStream->start($headers, Status::OK);

        // Send initial state
        $this->sseStream->sendJson(
            data: $initialState,
            event: 'pipeline.initial',
            id: '0'
        );

        // Stream updates
        $this->streamUpdates($pipelineId);
    }

    /**
     * Get current pipeline status snapshot (non-streaming)
     */
    #[Route(path: '/api/pipeline/status/{pipelineId}', method: Method::GET)]
    public function getPipelineStatus(string $pipelineId): JsonResult
    {
        $pipelineId = PipelineId::fromString($pipelineId);

        $state = $this->statusStore->getState($pipelineId);

        if ($state === null) {
            return new JsonResult(
                data: ['error' => 'Pipeline not found'],
                status: Status::NOT_FOUND
            );
        }

        return new JsonResult(data: $state);
    }

    /**
     * Stream pipeline updates until completion or timeout
     */
    private function streamUpdates(PipelineId $pipelineId): void
    {
        $eventId = 0;
        $maxDuration = 300; // 5 minutes max
        $startTime = time();
        $pollInterval = 1; // Poll every second

        while (time() - $startTime < $maxDuration) {
            // Check if client disconnected
            if (!$this->sseStream->isConnectionActive()) {
                $this->logger->info('Client disconnected from pipeline stream', [
                    'pipeline_id' => $pipelineId->value
                ]);
                break;
            }

            // Get current state
            $state = $this->statusStore->getState($pipelineId);

            if ($state === null) {
                // Pipeline was removed (completed and cleaned up)
                $this->sseStream->sendJson(
                    data: ['message' => 'Pipeline completed and cleaned up'],
                    event: 'pipeline.completed',
                    id: (string) ++$eventId
                );
                break;
            }

            // Send state update
            $this->sseStream->sendJson(
                data: $state,
                event: 'pipeline.status',
                id: (string) ++$eventId
            );

            // Check if pipeline reached terminal state
            if (in_array($state['status'], ['success', 'failed', 'rolled_back', 'cancelled'], true)) {
                $this->sseStream->sendJson(
                    data: [
                        'status' => $state['status'],
                        'final' => true,
                        'message' => 'Pipeline execution completed'
                    ],
                    event: 'pipeline.completed',
                    id: (string) ++$eventId
                );
                break;
            }

            // Send heartbeat
            $this->sseStream->sendHeartbeat(id: (string) $eventId);

            // Wait before next poll
            sleep($pollInterval);
        }

        // Close stream gracefully
        $this->sseStream->close('Stream ended');
    }
}
