<?php

declare(strict_types=1);

namespace App\Framework\Discovery\ValueObjects;

use App\Framework\Core\ValueObjects\Duration;

/**
 * Configuration value object for Discovery service
 *
 * Encapsulates all discovery-related configuration in a single immutable object
 * to reduce constructor complexity and improve maintainability.
 */
final readonly class DiscoveryConfiguration
{
    public readonly Duration $cacheTimeout;

    public function __construct(
        /** @var array<int, string> $paths */
        public array $paths = [],
        /** @var array<class-string, string> $attributeMappers */
        public array $attributeMappers = [],
        /** @var array<int, class-string> $targetInterfaces */
        public array $targetInterfaces = [],
        public bool $useCache = true,
        ?Duration $cacheTimeout = null,
        public ?string $contextSuffix = null,
        public int $memoryLimitMB = 128,
        public bool $enableEventDispatcher = true,
        public bool $enableMemoryMonitoring = true,
        public bool $enablePerformanceTracking = true,
        public int $maxFilesPerBatch = 100,
        public float $memoryPressureThreshold = 0.8
    ) {
        // Set default cache timeout if not provided
        $this->cacheTimeout = $cacheTimeout ?? Duration::fromHours(24);
    }

    /**
     * Create configuration for development environment
     */
    public static function development(): self
    {
        return new self(
            useCache                 : true, // Disable cache for development
            memoryLimitMB            : 512, // Much higher memory limit for development
            enableMemoryMonitoring   : false,
            enablePerformanceTracking: true, // Disable memory monitoring for development to avoid false positives
            maxFilesPerBatch         : 500, // Larger batches to ensure complete discovery
            memoryPressureThreshold  : 0.95 // Much more relaxed memory pressure threshold for development
        );
    }

    /**
     * Create configuration for production environment
     */
    public static function production(): self
    {
        return new self(
            useCache: true,
            cacheTimeout: Duration::fromHours(24),
            memoryLimitMB: 128,
            enablePerformanceTracking: false, // Disable for performance
            maxFilesPerBatch: 200 // Larger batches for better performance
        );
    }

    /**
     * Create configuration for testing environment
     *
     * Testing configuration now uses similar settings to development
     * to ensure consistent discovery behavior, especially for CLI commands
     */
    public static function testing(): self
    {
        return new self(
            useCache: false, // Disable cache for testing to ensure fresh discovery
            memoryLimitMB: 256, // Increased memory limit for comprehensive discovery
            enableEventDispatcher: false, // Disable events for testing
            enableMemoryMonitoring: false,
            enablePerformanceTracking: false,
            maxFilesPerBatch: 200, // Larger batches like development/production
            memoryPressureThreshold: 0.9 // More relaxed memory pressure threshold
        );
    }

    /**
     * Create configuration with specific paths (factory method)
     */
    /**
     * Create configuration with specific paths (factory method)
     * @param array<int, string> $paths
     */
    public static function forPaths(array $paths): self
    {
        return new self(paths: $paths);
    }

    /**
     * Create configuration with specific mappers
     */
    /**
     * Create configuration with specific mappers
     * @param array<class-string, string> $attributeMappers
     * @param array<int, class-string> $targetInterfaces
     */
    public static function withMappers(array $attributeMappers, array $targetInterfaces = []): self
    {
        return new self(
            attributeMappers: $attributeMappers,
            targetInterfaces: $targetInterfaces
        );
    }

    /**
     * Create a new configuration with modified cache settings
     */
    public function withCache(bool $useCache, ?Duration $timeout = null): self
    {
        return new self(
            paths: $this->paths,
            attributeMappers: $this->attributeMappers,
            targetInterfaces: $this->targetInterfaces,
            useCache: $useCache,
            cacheTimeout: $timeout ?? $this->cacheTimeout,
            contextSuffix: $this->contextSuffix,
            memoryLimitMB: $this->memoryLimitMB,
            enableEventDispatcher: $this->enableEventDispatcher,
            enableMemoryMonitoring: $this->enableMemoryMonitoring,
            enablePerformanceTracking: $this->enablePerformanceTracking,
            maxFilesPerBatch: $this->maxFilesPerBatch,
            memoryPressureThreshold: $this->memoryPressureThreshold
        );
    }

    /**
     * Create a new configuration with modified memory settings
     */
    public function withMemorySettings(int $limitMB, float $pressureThreshold = 0.8): self
    {
        return new self(
            paths: $this->paths,
            attributeMappers: $this->attributeMappers,
            targetInterfaces: $this->targetInterfaces,
            useCache: $this->useCache,
            cacheTimeout: $this->cacheTimeout,
            contextSuffix: $this->contextSuffix,
            memoryLimitMB: $limitMB,
            enableEventDispatcher: $this->enableEventDispatcher,
            enableMemoryMonitoring: $this->enableMemoryMonitoring,
            enablePerformanceTracking: $this->enablePerformanceTracking,
            maxFilesPerBatch: $this->maxFilesPerBatch,
            memoryPressureThreshold: $pressureThreshold
        );
    }

    /**
     * Create a new configuration with modified context suffix
     */
    public function withContextSuffix(string $suffix): self
    {
        return new self(
            paths: $this->paths,
            attributeMappers: $this->attributeMappers,
            targetInterfaces: $this->targetInterfaces,
            useCache: $this->useCache,
            cacheTimeout: $this->cacheTimeout,
            contextSuffix: $suffix,
            memoryLimitMB: $this->memoryLimitMB,
            enableEventDispatcher: $this->enableEventDispatcher,
            enableMemoryMonitoring: $this->enableMemoryMonitoring,
            enablePerformanceTracking: $this->enablePerformanceTracking,
            maxFilesPerBatch: $this->maxFilesPerBatch,
            memoryPressureThreshold: $this->memoryPressureThreshold
        );
    }

    /**
     * Create a new configuration with modified paths
     */
    /**
     * Create a new configuration with modified paths
     * @param array<int, string> $paths
     */
    public function withPaths(array $paths): self
    {
        return new self(
            paths: $paths,
            attributeMappers: $this->attributeMappers,
            targetInterfaces: $this->targetInterfaces,
            useCache: $this->useCache,
            cacheTimeout: $this->cacheTimeout,
            contextSuffix: $this->contextSuffix,
            memoryLimitMB: $this->memoryLimitMB,
            enableEventDispatcher: $this->enableEventDispatcher,
            enableMemoryMonitoring: $this->enableMemoryMonitoring,
            enablePerformanceTracking: $this->enablePerformanceTracking,
            maxFilesPerBatch: $this->maxFilesPerBatch,
            memoryPressureThreshold: $this->memoryPressureThreshold
        );
    }

    /**
     * Validate configuration settings
     */
    public function validate(): void
    {
        if ($this->memoryLimitMB < 32) {
            throw new \InvalidArgumentException('Memory limit must be at least 32MB');
        }

        if ($this->memoryPressureThreshold < 0.1 || $this->memoryPressureThreshold > 1.0) {
            throw new \InvalidArgumentException('Memory pressure threshold must be between 0.1 and 1.0');
        }

        if ($this->maxFilesPerBatch < 1) {
            throw new \InvalidArgumentException('Max files per batch must be at least 1');
        }

        if ($this->cacheTimeout->toSeconds() < 60) {
            throw new \InvalidArgumentException('Cache timeout must be at least 60 seconds');
        }
    }

    /**
     * Get memory limit in bytes
     */
    public function getMemoryLimitBytes(): int
    {
        return $this->memoryLimitMB * 1024 * 1024;
    }

    /**
     * Check if feature is enabled
     */
    public function isFeatureEnabled(string $feature): bool
    {
        return match ($feature) {
            'cache' => $this->useCache,
            'events' => $this->enableEventDispatcher,
            'memory_monitoring' => $this->enableMemoryMonitoring,
            'performance_tracking' => $this->enablePerformanceTracking,
            default => false
        };
    }

    /**
     * Convert to array for debugging/logging
     */
    /**
     * Convert to array for debugging/logging
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        return [
            'paths_count' => count($this->paths),
            'attribute_mappers_count' => count($this->attributeMappers),
            'target_interfaces_count' => count($this->targetInterfaces),
            'use_cache' => $this->useCache,
            'cache_timeout_seconds' => $this->cacheTimeout->toSeconds(),
            'context_suffix' => $this->contextSuffix,
            'memory_limit_mb' => $this->memoryLimitMB,
            'enable_event_dispatcher' => $this->enableEventDispatcher,
            'enable_memory_monitoring' => $this->enableMemoryMonitoring,
            'enable_performance_tracking' => $this->enablePerformanceTracking,
            'max_files_per_batch' => $this->maxFilesPerBatch,
            'memory_pressure_threshold' => $this->memoryPressureThreshold,
        ];
    }
}
