<?php

declare(strict_types=1);

namespace App\Framework\Database\Cache;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Database\Cache\ValueObjects\CacheMetricsSnapshot;
use App\Framework\Database\Cache\ValueObjects\CacheOverviewMetrics;
use App\Framework\Database\Cache\ValueObjects\CachePerformanceMetrics;
use App\Framework\Database\Cache\ValueObjects\CacheRecommendation;
use App\Framework\Database\Cache\ValueObjects\EntityCacheMetrics;
use App\Framework\Database\Cache\ValueObjects\EntityMetrics;
use App\Framework\Database\Cache\ValueObjects\MemoryUsageMetrics;
use App\Framework\Database\Cache\ValueObjects\QueryCacheMetrics;
use App\Framework\Database\Cache\ValueObjects\QueryMetrics;
use App\Framework\Database\Cache\ValueObjects\RecommendationCategory;
use App\Framework\Database\Cache\ValueObjects\RecommendationImpact;
use App\Framework\Database\Cache\ValueObjects\RecommendationType;
use App\Framework\Database\Cache\ValueObjects\RegionMetrics;
use App\Framework\Database\Events\Timestamp;
use App\Framework\DateTime\Clock;
use App\Framework\Performance\MemoryMonitor;

/**
 * Cache performance metrics and monitoring
 */
final class CacheMetrics
{
    private array $entityMetrics = [];

    private array $queryMetrics = [];

    private int $totalOperations = 0;

    private Timestamp $startTime;

    public function __construct(
        private readonly SecondLevelCacheInterface $cache,
        private readonly EntityCacheManager $cacheManager,
        private readonly Clock $clock,
        private readonly MemoryMonitor $memoryMonitor
    ) {
        $this->startTime = Timestamp::fromClock($this->clock);
    }

    /**
     * Get comprehensive cache metrics
     */
    public function getMetrics(): CacheMetricsSnapshot
    {
        $cacheStats = $this->cache->getStats();
        $regionStats = $this->cache->getRegionStats();

        return new CacheMetricsSnapshot(
            overview: $this->getOverviewMetrics($cacheStats),
            performance: $this->getPerformanceMetrics($cacheStats),
            entities: $this->getEntityMetrics(),
            queries: $this->getQueryMetrics(),
            regions: $this->getRegionMetrics($regionStats),
            recommendations: $this->getRecommendations($cacheStats)
        );
    }

    /**
     * Get cache overview metrics
     */
    private function getOverviewMetrics(array $cacheStats): CacheOverviewMetrics
    {
        $totalHits = $cacheStats['hits'] + $cacheStats['query_hits'];
        $totalMisses = $cacheStats['misses'] + $cacheStats['query_misses'];
        $totalRequests = $totalHits + $totalMisses;

        $hitRatio = $totalRequests > 0
            ? Percentage::fromRatio($totalHits, $totalRequests)
            : Percentage::zero();

        $uptime = $this->startTime->age($this->clock);

        $requestsPerSecond = $uptime->toSeconds() > 0
            ? $totalRequests / $uptime->toSeconds()
            : 0.0;

        return new CacheOverviewMetrics(
            enabled: $this->cacheManager->isEnabled(),
            totalRequests: $totalRequests,
            totalHits: $totalHits,
            totalMisses: $totalMisses,
            hitRatio: $hitRatio,
            uptime: $uptime,
            requestsPerSecond: $requestsPerSecond
        );
    }

    /**
     * Get cache performance metrics
     */
    private function getPerformanceMetrics(array $cacheStats): CachePerformanceMetrics
    {
        $entityHitRatio = ($cacheStats['hits'] + $cacheStats['misses']) > 0
            ? Percentage::fromRatio($cacheStats['hits'], $cacheStats['hits'] + $cacheStats['misses'])
            : Percentage::zero();

        $queryHitRatio = ($cacheStats['query_hits'] + $cacheStats['query_misses']) > 0
            ? Percentage::fromRatio($cacheStats['query_hits'], $cacheStats['query_hits'] + $cacheStats['query_misses'])
            : Percentage::zero();

        return new CachePerformanceMetrics(
            entityCache: new EntityCacheMetrics(
                hits: $cacheStats['hits'],
                misses: $cacheStats['misses'],
                hitRatio: $entityHitRatio,
                puts: $cacheStats['puts'],
                evictions: $cacheStats['evictions']
            ),
            queryCache: new QueryCacheMetrics(
                hits: $cacheStats['query_hits'],
                misses: $cacheStats['query_misses'],
                hitRatio: $queryHitRatio
            ),
            memory: $this->getMemoryMetrics()
        );
    }

    /**
     * Get memory usage metrics
     */
    private function getMemoryMetrics(): MemoryUsageMetrics
    {
        $currentUsage = $this->memoryMonitor->getCurrentMemory();
        $peakUsage = $this->memoryMonitor->getPeakMemory();
        $memoryLimit = $this->memoryMonitor->getMemoryLimit();

        $usagePercentage = $memoryLimit->isEmpty()
            ? Percentage::zero()
            : Percentage::fromDecimal($currentUsage->toBytes() / $memoryLimit->toBytes());

        return new MemoryUsageMetrics(
            current: $currentUsage,
            peak: $peakUsage,
            limit: $memoryLimit,
            usagePercentage: $usagePercentage
        );
    }

    /**
     * Get entity-specific metrics
     */
    private function getEntityMetrics(): array
    {
        $metrics = [];

        foreach ($this->entityMetrics as $entityClass => $stats) {
            $totalAccess = $stats['hits'] + $stats['misses'];
            $hitRatio = $totalAccess > 0
                ? Percentage::fromRatio($stats['hits'], $totalAccess)
                : Percentage::zero();

            $avgAccessTime = $stats['access_count'] > 0
                ? Duration::fromSeconds($stats['total_access_time'] / $stats['access_count'])
                : Duration::zero();

            $metrics[$entityClass] = new EntityMetrics(
                hits: $stats['hits'],
                misses: $stats['misses'],
                hitRatio: $hitRatio,
                cacheSize: $stats['cached_count'] ?? 0,
                averageAccessTime: $avgAccessTime
            );
        }

        return $metrics;
    }

    /**
     * Get query-specific metrics
     */
    private function getQueryMetrics(): QueryMetrics
    {
        return new QueryMetrics(
            totalCachedQueries: count($this->queryMetrics),
            topQueries: $this->getTopQueries(10),
            slowQueries: $this->getSlowQueries(5)
        );
    }

    /**
     * Get region-specific metrics
     */
    private function getRegionMetrics(array $regionStats): array
    {
        $metrics = [];

        foreach ($regionStats as $regionName => $stats) {
            $metrics[$regionName] = new RegionMetrics(
                name: $regionName,
                enabled: $stats['enabled'],
                defaultTtl: Duration::fromSeconds($stats['default_ttl']),
                maxSize: $stats['max_size'],
                efficiencyScore: Percentage::from($this->calculateRegionEfficiency($regionName))
            );
        }

        return $metrics;
    }

    /**
     * Get cache optimization recommendations
     */
    private function getRecommendations(array $cacheStats): array
    {
        $recommendations = [];
        $hitRatio = Percentage::fromDecimal($this->cacheManager->getCacheHitRatio());

        // Low hit ratio
        if ($hitRatio->isBelow(Percentage::from(30.0))) {
            $recommendations[] = new CacheRecommendation(
                type: RecommendationType::WARNING,
                category: RecommendationCategory::PERFORMANCE,
                message: 'Low cache hit ratio (<30%). Consider adjusting TTL or cache strategies.',
                impact: RecommendationImpact::HIGH
            );
        }

        // High eviction rate
        $evictionRate = $cacheStats['puts'] > 0
            ? Percentage::fromRatio($cacheStats['evictions'], $cacheStats['puts'])
            : Percentage::zero();

        if ($evictionRate->isAbove(Percentage::from(20.0))) {
            $recommendations[] = new CacheRecommendation(
                type: RecommendationType::WARNING,
                category: RecommendationCategory::MEMORY,
                message: 'High eviction rate. Consider increasing cache size or reducing TTL.',
                impact: RecommendationImpact::MEDIUM
            );
        }

        // Good performance
        if ($hitRatio->isAbove(Percentage::from(80.0))) {
            $recommendations[] = new CacheRecommendation(
                type: RecommendationType::SUCCESS,
                category: RecommendationCategory::PERFORMANCE,
                message: 'Excellent cache performance! Hit ratio >80%.',
                impact: RecommendationImpact::POSITIVE
            );
        }

        return $recommendations;
    }

    /**
     * Record entity access for metrics
     */
    public function recordEntityAccess(string $entityClass, bool $cacheHit, ?Duration $accessTime = null): void
    {
        if (! isset($this->entityMetrics[$entityClass])) {
            $this->entityMetrics[$entityClass] = [
                'hits' => 0,
                'misses' => 0,
                'total_access_time' => 0.0,
                'access_count' => 0,
            ];
        }

        if ($cacheHit) {
            $this->entityMetrics[$entityClass]['hits']++;
        } else {
            $this->entityMetrics[$entityClass]['misses']++;
        }

        $accessTimeSeconds = $accessTime?->toSeconds() ?? 0.0;
        $this->entityMetrics[$entityClass]['total_access_time'] += $accessTimeSeconds;
        $this->entityMetrics[$entityClass]['access_count']++;

        $this->totalOperations++;
    }

    /**
     * Get top queries by access frequency
     */
    private function getTopQueries(int $limit): array
    {
        $queries = $this->queryMetrics;

        uasort($queries, function ($a, $b) {
            return ($b['hits'] + $b['misses']) <=> ($a['hits'] + $a['misses']);
        });

        return array_slice($queries, 0, $limit, true);
    }

    /**
     * Get slow queries by execution time
     */
    private function getSlowQueries(int $limit): array
    {
        $queries = $this->queryMetrics;

        uasort($queries, function ($a, $b) {
            $avgTimeA = $a['execution_count'] > 0 ? $a['total_execution_time'] / $a['execution_count'] : 0;
            $avgTimeB = $b['execution_count'] > 0 ? $b['total_execution_time'] / $b['execution_count'] : 0;

            return $avgTimeB <=> $avgTimeA;
        });

        return array_slice($queries, 0, $limit, true);
    }

    /**
     * Calculate region efficiency score
     */
    private function calculateRegionEfficiency(string $regionName): float
    {
        // Simplified efficiency calculation
        return 75.0; // Placeholder
    }

    /**
     * Reset all metrics
     */
    public function reset(): void
    {
        $this->entityMetrics = [];
        $this->queryMetrics = [];
        $this->totalOperations = 0;
        $this->startTime = Timestamp::fromClock($this->clock);
    }
}
