<?php

declare(strict_types=1);

namespace App\Framework\Development\HotReload;

use App\Framework\Attributes\Singleton;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Filesystem\FileWatcher;
use App\Framework\Http\Headers;
use App\Framework\Http\SseStream;
use App\Framework\Http\Status;
use Psr\Log\LoggerInterface;

/**
 * Hot Reload Server for Development
 * Watches for file changes and notifies the browser to reload
 */
#[Singleton]
final readonly class HotReloadServer
{
    private const array DEFAULT_WATCH_PATTERNS = [
        'src/**/*.php',
        'resources/views/**/*.php',
        'resources/views/**/*.view.php',
        'config/**/*.php',
    ];

    private const array DEFAULT_IGNORE_PATTERNS = [
        'vendor/**',
        'var/**',
        'storage/**',
        'tests/**',
        'public/**',
    ];

    public function __construct(
        private FileWatcher $fileWatcher,
        private SseStream $sseStream,
        private ?LoggerInterface $logger = null
    ) {
    }

    /**
     * Start the hot reload server
     */
    public function start(): void
    {
        $this->logger?->info('Starting Hot Reload Server', [
            'watch_patterns' => self::DEFAULT_WATCH_PATTERNS,
            'ignore_patterns' => self::DEFAULT_IGNORE_PATTERNS,
        ]);

        // Setup file watcher
        $this->fileWatcher->watch(
            patterns: self::DEFAULT_WATCH_PATTERNS,
            ignore: self::DEFAULT_IGNORE_PATTERNS,
            callback: $this->handleFileChange(...),
            pollInterval: Duration::fromMilliseconds(500)
        );

        // Start SSE stream for browser communication
        $headers = new Headers([
            'Content-Type' => 'text/event-stream',
            'Cache-Control' => 'no-cache',
            'Connection' => 'keep-alive',
            'X-Accel-Buffering' => 'no', // Nginx
        ]);

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

    /**
     * Handle file change events
     */
    private function handleFileChange(FileChangeEvent $event): void
    {
        $this->logger?->debug('File change detected', [
            'file' => $event->getPath(),
            'type' => $event->getType()->value,
            'timestamp' => $event->getTimestamp()->format('c'),
        ]);

        // Determine reload type based on file
        $reloadType = $this->determineReloadType($event);

        // Send reload event to browser
        $this->sseStream->sendJson([
            'type' => $reloadType->value,
            'file' => $event->getPath(),
            'timestamp' => $event->getTimestamp()->format('c'),
        ], 'reload');

        // Clear caches if needed
        if ($this->shouldClearCache($event)) {
            $this->clearCaches();
        }
    }

    /**
     * Determine the type of reload needed
     */
    private function determineReloadType(FileChangeEvent $event): ReloadType
    {
        $path = $event->getPath();

        // PHP files need full page reload
        if (str_ends_with($path, '.php')) {
            return ReloadType::FULL;
        }

        // CSS files can use hot replacement
        if (str_ends_with($path, '.css')) {
            return ReloadType::CSS;
        }

        // JS modules can use HMR if supported
        if (str_ends_with($path, '.js') || str_ends_with($path, '.ts')) {
            return ReloadType::HMR;
        }

        // Templates need full reload
        if (str_ends_with($path, '.view.php')) {
            return ReloadType::FULL;
        }

        return ReloadType::FULL;
    }

    /**
     * Check if caches should be cleared
     */
    private function shouldClearCache(FileChangeEvent $event): bool
    {
        $path = $event->getPath();

        // Clear cache for config changes
        if (str_contains($path, '/config/')) {
            return true;
        }

        // Clear cache for route changes
        if (str_contains($path, 'Route') || str_contains($path, 'Controller')) {
            return true;
        }

        // Clear cache for view changes
        if (str_contains($path, '/views/')) {
            return true;
        }

        return false;
    }

    /**
     * Clear relevant caches
     */
    private function clearCaches(): void
    {
        $this->logger?->debug('Clearing development caches');

        // Clear discovery cache
        $discoveryCache = __DIR__ . '/../../../../var/cache/discovery';
        if (is_dir($discoveryCache)) {
            array_map('unlink', glob("$discoveryCache/*"));
        }

        // Clear view cache
        $viewCache = __DIR__ . '/../../../../var/cache/views';
        if (is_dir($viewCache)) {
            array_map('unlink', glob("$viewCache/*"));
        }

        // Clear route cache
        $routeCache = __DIR__ . '/../../../../var/cache/routes';
        if (is_dir($routeCache)) {
            array_map('unlink', glob("$routeCache/*"));
        }
    }

    /**
     * Stop the hot reload server
     */
    public function stop(): void
    {
        $this->logger?->info('Stopping Hot Reload Server');
        $this->fileWatcher->stop();
        $this->sseStream->close();
    }
}
