<?php

declare(strict_types=1);

namespace Tests\Framework\Discovery;

use App\Framework\Cache\Cache;
use App\Framework\Cache\Driver\InMemoryCache;
use App\Framework\Cache\GeneralCache;
use App\Framework\Core\PathProvider;
use App\Framework\DateTime\SystemClock;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Discovery\UnifiedDiscoveryService;
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
use App\Framework\Reflection\CachedReflectionProvider;
use App\Framework\Serializer\Php\PhpSerializer;
use App\Framework\Serializer\Php\PhpSerializerConfig;

describe('UnifiedDiscoveryService', function () {
    beforeEach(function () {
        $cacheDriver = new InMemoryCache();
        $serializer = new PhpSerializer(PhpSerializerConfig::safe());
        $this->cache = new GeneralCache($cacheDriver, $serializer);
        $this->clock = new SystemClock();
        $this->pathProvider = new PathProvider('/home/michael/dev/michaelschiemer');
        $this->reflectionProvider = new CachedReflectionProvider();

        $this->configuration = new DiscoveryConfiguration(
            paths: ['/home/michael/dev/michaelschiemer/tests/fixtures'],
            useCache: false, // Disable cache for tests
            enableMemoryMonitoring: false,
            memoryLimitMB: 64,
            maxFilesPerBatch: 100
        );

        // Clear cache between tests
        $this->cache->clear();
    });

    it('can be instantiated with required dependencies', function () {
        $service = new UnifiedDiscoveryService(
            pathProvider: $this->pathProvider,
            cache: $this->cache,
            clock: $this->clock,
            reflectionProvider: $this->reflectionProvider,
            configuration: $this->configuration
        );

        expect($service)->toBeInstanceOf(UnifiedDiscoveryService::class);
    });

    it('performs discovery and returns DiscoveryRegistry', function () {
        $service = new UnifiedDiscoveryService(
            pathProvider: $this->pathProvider,
            cache: $this->cache,
            clock: $this->clock,
            reflectionProvider: $this->reflectionProvider,
            configuration: $this->configuration
        );

        $registry = $service->discover();

        expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
        expect($registry->attributes)->toBeInstanceOf(\App\Framework\Discovery\Results\AttributeRegistry::class);
        expect($registry->interfaces)->toBeInstanceOf(\App\Framework\Discovery\Results\InterfaceRegistry::class);
    });

    it('handles empty scan paths gracefully', function () {
        $emptyConfig = new DiscoveryConfiguration(
            paths: [],
            useCache: false,
            enableMemoryMonitoring: false,
            memoryLimitMB: 64,
            maxFilesPerBatch: 100
        );

        $service = new UnifiedDiscoveryService(
            pathProvider: $this->pathProvider,
            cache: $this->cache,
            clock: $this->clock,
            reflectionProvider: $this->reflectionProvider,
            configuration: $emptyConfig
        );

        $registry = $service->discover();

        expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
        // Empty paths should still return a valid registry, just empty
        expect($registry->attributes->getAll())->toBeArray();
    });

    it('supports incremental discovery', function () {
        $service = new UnifiedDiscoveryService(
            pathProvider: $this->pathProvider,
            cache: $this->cache,
            clock: $this->clock,
            reflectionProvider: $this->reflectionProvider,
            configuration: $this->configuration
        );

        $registry = $service->incrementalDiscover();

        expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
    });

    it('validates configuration on instantiation', function () {
        $invalidConfig = new DiscoveryConfiguration(
            paths: ['/nonexistent/path'],
            useCache: false,
            enableMemoryMonitoring: false,
            memoryLimitMB: -1, // Invalid memory limit
            maxFilesPerBatch: 0 // Invalid batch size
        );

        expect(function () {
            new UnifiedDiscoveryService(
                pathProvider: $this->pathProvider,
                cache: $this->cache,
                clock: $this->clock,
                reflectionProvider: $this->reflectionProvider,
                configuration: $invalidConfig
            );
        })->toThrow(\InvalidArgumentException::class);
    });

    it('handles memory management configuration', function () {
        $memoryConfig = new DiscoveryConfiguration(
            paths: ['/home/michael/dev/michaelschiemer/src'],
            useCache: false,
            enableMemoryMonitoring: true,
            memoryLimitMB: 32, // 32MB limit
            maxFilesPerBatch: 50
        );

        $service = new UnifiedDiscoveryService(
            pathProvider: $this->pathProvider,
            cache: $this->cache,
            clock: $this->clock,
            reflectionProvider: $this->reflectionProvider,
            configuration: $memoryConfig
        );

        expect($service)->toBeInstanceOf(UnifiedDiscoveryService::class);

        // Should not throw memory errors with management enabled
        $registry = $service->discover();
        expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
    });

    it('respects chunk size in configuration', function () {
        $smallChunkConfig = new DiscoveryConfiguration(
            paths: ['/home/michael/dev/michaelschiemer/tests'],
            useCache: false,
            enableMemoryMonitoring: false,
            memoryLimitMB: 64,
            maxFilesPerBatch: 5 // Very small batches
        );

        $service = new UnifiedDiscoveryService(
            pathProvider: $this->pathProvider,
            cache: $this->cache,
            clock: $this->clock,
            reflectionProvider: $this->reflectionProvider,
            configuration: $smallChunkConfig
        );

        $registry = $service->discover();

        expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
        // Should handle small chunks without issues
    });

    it('handles file system errors gracefully', function () {
        $invalidPathConfig = new DiscoveryConfiguration(
            paths: ['/completely/nonexistent/path'],
            useCache: false,
            enableMemoryMonitoring: false,
            memoryLimitMB: 64,
            maxFilesPerBatch: 100
        );

        $service = new UnifiedDiscoveryService(
            pathProvider: $this->pathProvider,
            cache: $this->cache,
            clock: $this->clock,
            reflectionProvider: $this->reflectionProvider,
            configuration: $invalidPathConfig
        );

        // Should not throw but return empty registry
        $registry = $service->discover();
        expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
    });
});
