<?php

declare(strict_types=1);

namespace App\Application\Admin\Infrastructure;

use App\Application\Admin\Service\AdminLayoutProcessor;
use App\Framework\Attributes\Route;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\Clock;
use App\Framework\DateTime\Timer;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\Method;
use App\Framework\Http\SseStream;
use App\Framework\Http\Status;
use App\Framework\Logging\LogViewer;
use App\Framework\Meta\MetaData;
use App\Framework\Router\Result\JsonResult;
use App\Framework\Router\Result\SseResult;
use App\Framework\Router\Result\ViewResult;

final readonly class LogViewerController
{
    public function __construct(
        private LogViewer $logViewer,
        private Timer $timer,
        private AdminLayoutProcessor $layoutProcessor,
        private Clock $clock,
    ) {
    }

    #[Route('/admin/infrastructure/logs', Method::GET)]
    public function showLogViewer(): ViewResult
    {
        $metaData = new MetaData(
            title: 'Log Viewer | Admin',
            description: 'Real-time log viewing and analysis'
        );

        $data = [
            'title' => 'Log Viewer',
            'pageClass' => 'log-viewer-page',
            'current_year' => $this->clock->now()->format('Y'),
        ];

        $finalData = $this->layoutProcessor->processLayoutFromArray($data);

        return new ViewResult('log-viewer', $metaData, $finalData);
    }

    #[Route('/admin/infrastructure/logs/api/list', Method::GET)]
    /**
     * @return JsonResult<array{status: string, data: array{logs: array<string, mixed>, total_logs: int}}>
     */
    public function getAvailableLogs(): JsonResult
    {
        $logs = $this->logViewer->getAvailableLogs();

        return new JsonResult([
            'status' => 'success',
            'data' => [
                'logs' => $logs,
                'total_logs' => count($logs),
            ],
        ]);
    }

    #[Route('/admin/infrastructure/logs/api/read/{logName}', Method::GET)]
    /**
     * @return JsonResult<array{status: string, data?: array<string, mixed>, message?: string, available_logs?: array<int, string>}>
     */
    public function readLog(string $logName, HttpRequest $request): JsonResult
    {
        $limit = $request->query->getInt('limit', 100);
        $level = $request->query->get('level');
        $search = $request->query->get('search');

        try {
            $logData = $this->logViewer->readLog($logName, $limit, $level, $search);

            return new JsonResult([
                'status' => 'success',
                'data' => $logData->toArray(),
            ]);

        } catch (\InvalidArgumentException $e) {
            return new JsonResult([
                'status' => 'error',
                'message' => $e->getMessage(),
                'available_logs' => array_keys($this->logViewer->getAvailableLogs()),
            ], Status::NOT_FOUND);
        }
    }

    #[Route('/admin/infrastructure/logs/api/tail/{logName}', Method::GET)]
    /**
     * @return JsonResult<array{status: string, data?: array<string, mixed>, message?: string}>
     */
    public function tailLog(string $logName, HttpRequest $request): JsonResult
    {
        $lines = $request->query->getInt('lines', 50);

        try {
            $logData = $this->logViewer->tailLog($logName, $lines);

            return new JsonResult([
                'status' => 'success',
                'data' => $logData->toArray(),
            ]);

        } catch (\InvalidArgumentException $e) {
            return new JsonResult([
                'status' => 'error',
                'message' => $e->getMessage(),
            ],  Status::NOT_FOUND);
        }
    }

    #[Route('/admin/infrastructure/logs/api/search', Method::GET)]
    /**
     * @return JsonResult<array{status: string, data?: array<string, mixed>, message?: string}>
     */
    public function searchLogs(HttpRequest $request): JsonResult
    {
        $query = $request->query->get('query', '');
        $level = $request->query->get('level');
        $logsParam = $request->query->get('logs');
        $logs = $logsParam ? explode(',', $logsParam) : null;

        if (empty($query)) {
            return new JsonResult([
                'status' => 'error',
                'message' => 'Query parameter is required',
            ], Status::BAD_REQUEST);
        }

        $results = $this->logViewer->searchLogs($query, $logs, $level);

        return new JsonResult([
            'status' => 'success',
            'data' => $results->toArray(),
        ]);
    }

    #[Route('/admin/infrastructure/logs/api/levels', Method::GET)]
    /**
     * @return JsonResult<array{status: string, data: array{levels: array<string, array{color: string, icon: string}>}}>
     */
    public function getLogLevels(): JsonResult
    {
        return new JsonResult([
            'status' => 'success',
            'data' => [
                'levels' => [
                    'DEBUG' => ['color' => '#6c757d', 'icon' => '🔍'],
                    'INFO' => ['color' => '#17a2b8', 'icon' => 'ℹ️'],
                    'WARNING' => ['color' => '#ffc107', 'icon' => '⚠️'],
                    'ERROR' => ['color' => '#dc3545', 'icon' => '❌'],
                    'CRITICAL' => ['color' => '#800020', 'icon' => '🚨'],
                ],
            ],
        ]);
    }

    #[Route('/admin/infrastructure/logs/api/stream/{logName}', Method::GET)]
    public function streamLog(string $logName, HttpRequest $request): SseResult
    {
        $level = $request->query->get('level');
        $search = $request->query->get('search');
        $batchSize = $request->query->getInt('batch_size', 10);

        try {
            static $executed = false;

            return new SseResult(
                callback: function (SseStream $stream) use ($logName, $level, $search, $batchSize, &$executed) {
                    // Verhindere mehrfache Ausführung
                    if ($executed || ! $stream->isActive()) {
                        return;
                    }
                    $executed = true;

                    foreach ($this->logViewer->streamLog($logName, $level, $search, $batchSize) as $batch) {
                        // Prüfe Verbindung vor jedem Batch
                        if (! $stream->isConnectionActive() || ! $stream->isActive()) {
                            break;
                        }

                        // Convert LogEntry objects to arrays for JSON
                        $batchData = array_map(
                            fn($entry) => $entry->toArray(),
                            $batch['batch']
                        );

                        $stream->sendJson([
                            'status' => 'success',
                            'batch' => $batchData,
                            'batch_number' => $batch['batch_number'],
                            'entries_in_batch' => $batch['entries_in_batch'],
                            'total_processed' => $batch['total_processed'],
                            'is_final' => $batch['is_final'] ?? false,
                        ], 'log_batch');

                        // Kleine Pause für bessere Performance
                        $this->timer->sleep(Duration::fromMilliseconds(50));
                    }

                    // Sende finales Event
                    if ($stream->isConnectionActive() && $stream->isActive()) {
                        $stream->sendJson(['status' => 'complete'], 'stream_complete');
                    }

                    // Stream beenden
                    $stream->close('Log streaming completed');
                }, // 1 Minute Maximum
                maxDuration: 60, // Heartbeat alle 10 Sekunden
                heartbeatInterval: 10
            );

        } catch (\InvalidArgumentException $e) {
            return new SseResult(callback: function (SseStream $stream) use ($e) {
                $stream->sendJson([
                    'status' => 'error',
                    'message' => $e->getMessage(),
                ], 'error');
            });
        }
    }
}
