<?php

declare(strict_types=1);

namespace App\Framework\Performance\Analysis;

use App\Framework\Core\ValueObjects\Score;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Performance\ValueObjects\PerformanceSnapshot;

/**
 * Analyzes performance snapshots to identify bottlenecks and optimization opportunities
 *
 * Provides intelligent analysis of performance data to identify
 * system bottlenecks, inefficiencies, and areas for improvement.
 */
final class BottleneckAnalyzer
{
    // Performance thresholds for different categories
    private const THRESHOLDS = [
        'duration' => [
            PerformanceCategory::DISCOVERY => 10.0, // 10 seconds
            PerformanceCategory::DATABASE => 1.0,   // 1 second
            PerformanceCategory::CACHE => 0.1,      // 100ms
            PerformanceCategory::FILESYSTEM => 2.0, // 2 seconds
            'default' => 5.0,
        ],
        'memory_mb' => [
            PerformanceCategory::DISCOVERY => 256,  // 256MB
            PerformanceCategory::DATABASE => 64,    // 64MB
            PerformanceCategory::CACHE => 32,       // 32MB
            PerformanceCategory::FILESYSTEM => 128, // 128MB
            'default' => 128,
        ],
        'throughput' => [
            PerformanceCategory::DISCOVERY => 10.0, // 10 items/sec
            PerformanceCategory::DATABASE => 100.0, // 100 ops/sec
            PerformanceCategory::CACHE => 1000.0,   // 1000 ops/sec
            PerformanceCategory::FILESYSTEM => 50.0, // 50 files/sec
            'default' => 50.0,
        ],
        'cache_hit_rate' => 0.8,  // 80% minimum
        'error_rate' => 0.05,     // 5% maximum
        'memory_pressure' => 0.8,  // 80% maximum
    ];

    /**
     * Analyze snapshot for bottlenecks
     */
    public function analyze(PerformanceSnapshot $snapshot): BottleneckAnalysis
    {
        $bottlenecks = [];
        $recommendations = [];
        $severity = 'info';

        // Duration analysis
        $durationBottleneck = $this->analyzeDuration($snapshot);
        if ($durationBottleneck !== null) {
            $bottlenecks[] = $durationBottleneck;
            $severity = $this->escalateSeverity($severity, $durationBottleneck['severity']);
        }

        // Memory analysis
        $memoryBottleneck = $this->analyzeMemory($snapshot);
        if ($memoryBottleneck !== null) {
            $bottlenecks[] = $memoryBottleneck;
            $severity = $this->escalateSeverity($severity, $memoryBottleneck['severity']);
        }

        // Throughput analysis
        $throughputBottleneck = $this->analyzeThroughput($snapshot);
        if ($throughputBottleneck !== null) {
            $bottlenecks[] = $throughputBottleneck;
            $severity = $this->escalateSeverity($severity, $throughputBottleneck['severity']);
        }

        // Cache performance analysis
        $cacheBottleneck = $this->analyzeCachePerformance($snapshot);
        if ($cacheBottleneck !== null) {
            $bottlenecks[] = $cacheBottleneck;
            $severity = $this->escalateSeverity($severity, $cacheBottleneck['severity']);
        }

        // Error rate analysis
        $errorBottleneck = $this->analyzeErrorRate($snapshot);
        if ($errorBottleneck !== null) {
            $bottlenecks[] = $errorBottleneck;
            $severity = $this->escalateSeverity($severity, $errorBottleneck['severity']);
        }

        // Generate recommendations
        $recommendations = $this->generateRecommendations($bottlenecks, $snapshot);

        // Calculate overall performance score
        $performanceScore = $this->calculatePerformanceScore($snapshot, $bottlenecks);

        return new BottleneckAnalysis(
            snapshot: $snapshot,
            bottlenecks: $bottlenecks,
            recommendations: $recommendations,
            severity: $severity,
            performanceScore: $performanceScore,
            analyzedAt: new \DateTimeImmutable()
        );
    }

    /**
     * Analyze multiple snapshots for patterns
     */
    public function analyzePattern(array $snapshots): array
    {
        if (empty($snapshots)) {
            return ['pattern' => 'no_data'];
        }

        $analyses = array_map([$this, 'analyze'], $snapshots);

        // Count bottleneck types
        $bottleneckTypes = [];
        $severityCounts = [];

        foreach ($analyses as $analysis) {
            foreach ($analysis->bottlenecks as $bottleneck) {
                $type = $bottleneck['type'];
                $bottleneckTypes[$type] = ($bottleneckTypes[$type] ?? 0) + 1;
            }

            $severity = $analysis->severity;
            $severityCounts[$severity] = ($severityCounts[$severity] ?? 0) + 1;
        }

        // Find most common bottlenecks
        arsort($bottleneckTypes);
        $topBottlenecks = array_slice($bottleneckTypes, 0, 3, true);

        // Calculate trend
        $performanceScores = array_map(
            fn ($analysis) => $analysis->performanceScore->toDecimal(),
            $analyses
        );
        $trend = $this->calculateTrend($performanceScores);

        return [
            'total_operations' => count($snapshots),
            'top_bottlenecks' => $topBottlenecks,
            'severity_distribution' => $severityCounts,
            'performance_trend' => $trend,
            'average_score' => array_sum($performanceScores) / count($performanceScores),
            'pattern_recommendations' => $this->generatePatternRecommendations($topBottlenecks, $trend),
        ];
    }

    /**
     * Analyze duration bottlenecks
     */
    private function analyzeDuration(PerformanceSnapshot $snapshot): ?array
    {
        if ($snapshot->duration === null) {
            return null;
        }

        $duration = $snapshot->duration->toSeconds();
        $threshold = $this->getThreshold('duration', $snapshot->category);

        if ($duration <= $threshold) {
            return null;
        }

        $severity = match (true) {
            $duration > $threshold * 3 => 'critical',
            $duration > $threshold * 2 => 'warning',
            default => 'info'
        };

        return [
            'type' => 'slow_execution',
            'severity' => $severity,
            'metric' => 'duration',
            'value' => $duration,
            'threshold' => $threshold,
            'impact' => 'High response times affect user experience',
            'description' => "Operation took {$duration}s (threshold: {$threshold}s)",
        ];
    }

    /**
     * Analyze memory bottlenecks
     */
    private function analyzeMemory(PerformanceSnapshot $snapshot): ?array
    {
        $memoryUsage = $snapshot->peakMemory->toMegabytes();
        $threshold = $this->getThreshold('memory_mb', $snapshot->category);
        $memoryPressure = $snapshot->getMemoryPressure();

        // Check both absolute usage and pressure
        $isHighUsage = $memoryUsage > $threshold;
        $isHighPressure = $memoryPressure > self::THRESHOLDS['memory_pressure'];

        if (! $isHighUsage && ! $isHighPressure) {
            return null;
        }

        $severity = match (true) {
            $memoryUsage > $threshold * 2 || $memoryPressure > 0.95 => 'critical',
            $memoryUsage > $threshold * 1.5 || $memoryPressure > 0.9 => 'warning',
            default => 'info'
        };

        $description = $isHighUsage
            ? "High memory usage: {$memoryUsage}MB (threshold: {$threshold}MB)"
            : "High memory pressure: " . round($memoryPressure * 100, 1) . "%";

        return [
            'type' => 'high_memory_usage',
            'severity' => $severity,
            'metric' => 'memory',
            'value' => $memoryUsage,
            'threshold' => $threshold,
            'pressure' => $memoryPressure,
            'impact' => 'High memory usage can lead to system instability',
            'description' => $description,
        ];
    }

    /**
     * Analyze throughput bottlenecks
     */
    private function analyzeThroughput(PerformanceSnapshot $snapshot): ?array
    {
        $throughput = $snapshot->getThroughput();
        $threshold = $this->getThreshold('throughput', $snapshot->category);

        if ($throughput >= $threshold || $snapshot->itemsProcessed === 0) {
            return null;
        }

        $severity = match (true) {
            $throughput < $threshold * 0.3 => 'critical',
            $throughput < $threshold * 0.5 => 'warning',
            default => 'info'
        };

        return [
            'type' => 'low_throughput',
            'severity' => $severity,
            'metric' => 'throughput',
            'value' => $throughput,
            'threshold' => $threshold,
            'impact' => 'Low throughput affects system capacity',
            'description' => "Low throughput: " . round($throughput, 2) . " items/s (threshold: {$threshold} items/s)",
        ];
    }

    /**
     * Analyze cache performance bottlenecks
     */
    private function analyzeCachePerformance(PerformanceSnapshot $snapshot): ?array
    {
        $totalCacheOps = $snapshot->cacheHits + $snapshot->cacheMisses;

        if ($totalCacheOps === 0) {
            return null; // No cache operations
        }

        $hitRate = $snapshot->getCacheHitRate();
        $threshold = self::THRESHOLDS['cache_hit_rate'];

        if ($hitRate >= $threshold) {
            return null;
        }

        $severity = match (true) {
            $hitRate < $threshold * 0.5 => 'critical',
            $hitRate < $threshold * 0.7 => 'warning',
            default => 'info'
        };

        return [
            'type' => 'poor_cache_performance',
            'severity' => $severity,
            'metric' => 'cache_hit_rate',
            'value' => $hitRate,
            'threshold' => $threshold,
            'impact' => 'Poor cache performance increases response times',
            'description' => "Low cache hit rate: " . round($hitRate * 100, 1) . "% (threshold: " . round($threshold * 100, 1) . "%)",
        ];
    }

    /**
     * Analyze error rate bottlenecks
     */
    private function analyzeErrorRate(PerformanceSnapshot $snapshot): ?array
    {
        if ($snapshot->itemsProcessed === 0) {
            return null;
        }

        $errorRate = $snapshot->getErrorRate();
        $threshold = self::THRESHOLDS['error_rate'];

        if ($errorRate <= $threshold) {
            return null;
        }

        $severity = match (true) {
            $errorRate > $threshold * 4 => 'critical',
            $errorRate > $threshold * 2 => 'warning',
            default => 'info'
        };

        return [
            'type' => 'high_error_rate',
            'severity' => $severity,
            'metric' => 'error_rate',
            'value' => $errorRate,
            'threshold' => $threshold,
            'impact' => 'High error rates indicate system reliability issues',
            'description' => "High error rate: " . round($errorRate * 100, 1) . "% (threshold: " . round($threshold * 100, 1) . "%)",
        ];
    }

    /**
     * Generate recommendations based on bottlenecks
     */
    private function generateRecommendations(array $bottlenecks, PerformanceSnapshot $snapshot): array
    {
        $recommendations = [];

        foreach ($bottlenecks as $bottleneck) {
            $recommendations[] = match ($bottleneck['type']) {
                'slow_execution' => $this->getExecutionRecommendations($snapshot),
                'high_memory_usage' => $this->getMemoryRecommendations($snapshot),
                'low_throughput' => $this->getThroughputRecommendations($snapshot),
                'poor_cache_performance' => $this->getCacheRecommendations($snapshot),
                'high_error_rate' => $this->getErrorRecommendations($snapshot),
                default => 'Review system configuration and optimize accordingly'
            };
        }

        return array_unique(array_filter($recommendations));
    }

    /**
     * Generate recommendations for execution performance
     */
    private function getExecutionRecommendations(PerformanceSnapshot $snapshot): string
    {
        return match ($snapshot->category) {
            PerformanceCategory::DISCOVERY => 'Consider enabling parallel processing or reducing batch size',
            PerformanceCategory::DATABASE => 'Optimize queries, add indexes, or implement connection pooling',
            PerformanceCategory::CACHE => 'Review cache strategy and consider warming frequently accessed data',
            PerformanceCategory::FILESYSTEM => 'Optimize file I/O patterns or consider async processing',
            default => 'Profile and optimize the slowest code paths'
        };
    }

    /**
     * Generate recommendations for memory usage
     */
    private function getMemoryRecommendations(PerformanceSnapshot $snapshot): string
    {
        return match ($snapshot->category) {
            PerformanceCategory::DISCOVERY => 'Enable memory-aware processing and increase cleanup frequency',
            PerformanceCategory::DATABASE => 'Reduce result set sizes or implement streaming',
            PerformanceCategory::CACHE => 'Implement cache eviction policies and compression',
            default => 'Review memory allocation patterns and implement cleanup strategies'
        };
    }

    /**
     * Generate recommendations for throughput
     */
    private function getThroughputRecommendations(PerformanceSnapshot $snapshot): string
    {
        return match ($snapshot->category) {
            PerformanceCategory::DISCOVERY => 'Enable concurrent processing and optimize file scanning',
            PerformanceCategory::DATABASE => 'Implement batch operations and optimize queries',
            PerformanceCategory::CACHE => 'Review cache configuration and consider clustering',
            default => 'Implement parallel processing and optimize algorithms'
        };
    }

    /**
     * Generate recommendations for cache performance
     */
    private function getCacheRecommendations(PerformanceSnapshot $snapshot): string
    {
        return 'Review cache strategy, implement cache warming, and optimize cache keys';
    }

    /**
     * Generate recommendations for error rates
     */
    private function getErrorRecommendations(PerformanceSnapshot $snapshot): string
    {
        return 'Investigate error causes, improve error handling, and add input validation';
    }

    /**
     * Generate pattern-based recommendations
     */
    private function generatePatternRecommendations(array $topBottlenecks, string $trend): array
    {
        $recommendations = [];

        // Recommendations based on most common bottlenecks
        foreach (array_keys($topBottlenecks) as $bottleneck) {
            $recommendations[] = match ($bottleneck) {
                'slow_execution' => 'Implement performance monitoring and systematic optimization',
                'high_memory_usage' => 'Review memory management strategy across the system',
                'low_throughput' => 'Consider architectural changes for better scalability',
                'poor_cache_performance' => 'Implement comprehensive caching strategy review',
                'high_error_rate' => 'Improve system reliability and error handling',
                default => 'Monitor and optimize the most frequently occurring bottleneck'
            };
        }

        // Recommendations based on trend
        if ($trend === 'degrading') {
            $recommendations[] = 'Performance is degrading - investigate recent changes and resource usage';
        } elseif ($trend === 'stable_poor') {
            $recommendations[] = 'Consistent performance issues detected - consider infrastructure upgrades';
        }

        return array_unique($recommendations);
    }

    /**
     * Calculate overall performance score
     */
    private function calculatePerformanceScore(PerformanceSnapshot $snapshot, array $bottlenecks): Score
    {
        $baseScore = 100;

        // Deduct points based on bottlenecks
        foreach ($bottlenecks as $bottleneck) {
            $deduction = match ($bottleneck['severity']) {
                'critical' => 30,
                'warning' => 15,
                'info' => 5,
                default => 0
            };
            $baseScore -= $deduction;
        }

        // Bonus for good performance indicators
        if ($snapshot->getErrorRate() === 0.0) {
            $baseScore += 5;
        }

        if ($snapshot->getCacheHitRate() > 0.9) {
            $baseScore += 5;
        }

        return Score::fromRatio(max(0, min(100, $baseScore)), 100);
    }

    /**
     * Get threshold for category
     */
    private function getThreshold(string $metric, PerformanceCategory $category): float
    {
        return self::THRESHOLDS[$metric][$category] ?? self::THRESHOLDS[$metric]['default'];
    }

    /**
     * Escalate severity level
     */
    private function escalateSeverity(string $current, string $new): string
    {
        $levels = ['info' => 1, 'warning' => 2, 'critical' => 3];

        return $levels[$new] > $levels[$current] ? $new : $current;
    }

    /**
     * Calculate trend for performance scores
     */
    private function calculateTrend(array $scores): string
    {
        if (count($scores) < 3) {
            return 'insufficient_data';
        }

        $midpoint = (int) (count($scores) / 2);
        $firstHalf = array_slice($scores, 0, $midpoint);
        $secondHalf = array_slice($scores, $midpoint);

        $firstAvg = array_sum($firstHalf) / count($firstHalf);
        $secondAvg = array_sum($secondHalf) / count($secondHalf);

        $difference = $secondAvg - $firstAvg;

        if ($difference > 0.1) {
            return 'improving';
        } elseif ($difference < -0.1) {
            return $secondAvg < 0.5 ? 'stable_poor' : 'degrading';
        }

        return $secondAvg < 0.5 ? 'stable_poor' : 'stable';
    }
}
