<?php

declare(strict_types=1);

namespace App\Framework\Cache\Strategies;

use App\Framework\Cache\CacheKey;
use App\Framework\Core\ValueObjects\Duration;

/**
 * Cache Strategy Manager
 *
 * Orchestrates multiple cache strategies and coordinates their execution.
 * Provides a unified interface for strategy management and composition.
 */
final class CacheStrategyManager
{
    /** @var array<string, CacheStrategy> */
    private array $strategies = [];

    /** @var array<string> */
    private array $enabledStrategies = [];

    public function __construct(
        private readonly bool $enabled = true
    ) {
    }

    /**
     * Register a cache strategy
     */
    public function addStrategy(CacheStrategy $strategy): self
    {
        $name = $strategy->getName();
        $this->strategies[$name] = $strategy;

        if ($strategy->isEnabled()) {
            $this->enabledStrategies[] = $name;
        }

        return $this;
    }

    /**
     * Remove a strategy
     */
    public function removeStrategy(string $name): self
    {
        unset($this->strategies[$name]);
        $this->enabledStrategies = array_filter(
            $this->enabledStrategies,
            fn ($strategyName) => $strategyName !== $name
        );

        return $this;
    }

    /**
     * Enable a strategy
     */
    public function enableStrategy(string $name): self
    {
        if (isset($this->strategies[$name]) && ! in_array($name, $this->enabledStrategies)) {
            $this->enabledStrategies[] = $name;
        }

        return $this;
    }

    /**
     * Disable a strategy
     */
    public function disableStrategy(string $name): self
    {
        $this->enabledStrategies = array_filter(
            $this->enabledStrategies,
            fn ($strategyName) => $strategyName !== $name
        );

        return $this;
    }

    /**
     * Notify all enabled strategies of cache access
     */
    public function notifyAccess(CacheKey $key, bool $isHit, ?Duration $retrievalTime = null): void
    {
        if (! $this->enabled) {
            return;
        }

        foreach ($this->getEnabledStrategies() as $strategy) {
            try {
                $strategy->onCacheAccess($key, $isHit, $retrievalTime);
            } catch (\Throwable $e) {
                // Log error but don't break cache operations
                error_log("Cache strategy {$strategy->getName()} access notification failed: " . $e->getMessage());
            }
        }
    }

    /**
     * Notify all enabled strategies of cache set and get final TTL
     */
    public function notifySet(CacheKey $key, mixed $value, ?Duration $originalTtl): Duration
    {
        if (! $this->enabled) {
            return $originalTtl ?? Duration::fromHours(1);
        }

        $finalTtl = $originalTtl ?? Duration::fromHours(1);

        foreach ($this->getEnabledStrategies() as $strategy) {
            try {
                $finalTtl = $strategy->onCacheSet($key, $value, $finalTtl);
            } catch (\Throwable $e) {
                // Log error but continue with current TTL
                error_log("Cache strategy {$strategy->getName()} set notification failed: " . $e->getMessage());
            }
        }

        return $finalTtl;
    }

    /**
     * Notify all enabled strategies of cache forget
     */
    public function notifyForget(CacheKey $key): void
    {
        if (! $this->enabled) {
            return;
        }

        foreach ($this->getEnabledStrategies() as $strategy) {
            try {
                $strategy->onCacheForget($key);
            } catch (\Throwable $e) {
                // Log error but don't break cache operations
                error_log("Cache strategy {$strategy->getName()} forget notification failed: " . $e->getMessage());
            }
        }
    }

    /**
     * Get comprehensive statistics from all strategies
     */
    public function getStats(): array
    {
        $stats = [
            'strategy_manager' => [
                'enabled' => $this->enabled,
                'total_strategies' => count($this->strategies),
                'enabled_strategies' => count($this->enabledStrategies),
                'strategy_names' => array_keys($this->strategies),
                'enabled_strategy_names' => $this->enabledStrategies,
            ],
            'strategies' => [],
        ];

        foreach ($this->strategies as $name => $strategy) {
            try {
                $stats['strategies'][$name] = $strategy->getStats();
            } catch (\Throwable $e) {
                $stats['strategies'][$name] = [
                    'error' => 'Failed to get stats: ' . $e->getMessage(),
                ];
            }
        }

        return $stats;
    }

    /**
     * Clear all strategy data
     */
    public function clearAll(): void
    {
        foreach ($this->strategies as $strategy) {
            try {
                $strategy->clear();
            } catch (\Throwable $e) {
                error_log("Failed to clear strategy {$strategy->getName()}: " . $e->getMessage());
            }
        }
    }

    /**
     * Get a specific strategy by name
     */
    public function getStrategy(string $name): ?CacheStrategy
    {
        return $this->strategies[$name] ?? null;
    }

    /**
     * Get all registered strategies
     */
    public function getAllStrategies(): array
    {
        return $this->strategies;
    }

    /**
     * Get only enabled strategies
     */
    public function getEnabledStrategies(): array
    {
        $enabledStrategies = [];

        foreach ($this->enabledStrategies as $name) {
            if (isset($this->strategies[$name])) {
                $enabledStrategies[] = $this->strategies[$name];
            }
        }

        return $enabledStrategies;
    }

    /**
     * Check if a strategy is enabled
     */
    public function isStrategyEnabled(string $name): bool
    {
        return in_array($name, $this->enabledStrategies);
    }

    /**
     * Get strategy count
     */
    public function getStrategyCount(): int
    {
        return count($this->strategies);
    }

    /**
     * Check if manager is enabled
     */
    public function isEnabled(): bool
    {
        return $this->enabled;
    }

    /**
     * Create a pre-configured strategy manager with common strategies
     */
    public static function createDefault(): self
    {
        $manager = new self();

        // Add default strategies
        $manager->addStrategy(new AdaptiveTtlCacheStrategy());
        $manager->addStrategy(new HeatMapCacheStrategy());
        $manager->addStrategy(new PredictiveCacheStrategy());

        return $manager;
    }

    /**
     * Create a performance-focused strategy manager
     */
    public static function createPerformanceFocused(): self
    {
        $manager = new self();

        // Add performance-optimized strategies
        $manager->addStrategy(new AdaptiveTtlCacheStrategy(
            enabled: true,
            learningWindow: 50, // Smaller window for faster adaptation
            extensionFactor: 2.0, // More aggressive extension
            reductionFactor: 0.6   // More aggressive reduction
        ));

        $manager->addStrategy(new HeatMapCacheStrategy(
            enabled: true,
            maxTrackedKeys: 5000,  // Smaller tracking for performance
            hotThreshold: 30,      // Higher threshold for hot keys
            coldThreshold: 1       // Lower threshold for cold keys (must be int)
        ));

        return $manager;
    }

    /**
     * Create a development-focused strategy manager with detailed tracking
     */
    public static function createDevelopmentFocused(): self
    {
        $manager = new self();

        // Add development-friendly strategies with detailed tracking
        $manager->addStrategy(new AdaptiveTtlCacheStrategy(
            enabled: true,
            learningWindow: 200,   // Larger window for detailed analysis
            extensionFactor: 1.3,  // Conservative extension
            reductionFactor: 0.9   // Conservative reduction
        ));

        $manager->addStrategy(new HeatMapCacheStrategy(
            enabled: true,
            maxTrackedKeys: 20000, // More detailed tracking
            hotThreshold: 10,      // Lower threshold to catch more patterns
            coldThreshold: 2,      // Higher threshold for cold detection
            analysisWindowHours: 6 // Longer analysis window
        ));

        $manager->addStrategy(new PredictiveCacheStrategy(
            enabled: true,
            predictionWindowHours: 48, // Longer prediction window
            confidenceThreshold: 0.5,  // Lower threshold for more predictions
            maxConcurrentWarming: 10   // More concurrent warming
        ));

        return $manager;
    }
}
