<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Core\ValueObjects;

/**
 * Optimization Result Value Object
 *
 * Contains the results of data optimization including metrics,
 * applied optimizations, and metadata for analysis.
 */
final readonly class OptimizationResult
{
    public function __construct(
        public mixed $originalData,
        public mixed $optimizedData,
        public int $originalSize,
        public int $optimizedSize,
        public float $compressionRatio,
        public OptimizationStrategy $strategy,
        public OutputFormat $format,
        public array $optimizations,
        public array $metadata
    ) {
    }

    public function toArray(): array
    {
        return [
            'optimization_summary' => [
                'strategy' => $this->strategy->value,
                'format' => $this->format->value,
                'original_size' => $this->originalSize,
                'optimized_size' => $this->optimizedSize,
                'size_reduction' => $this->getSizeReduction(),
                'size_reduction_percentage' => $this->getSizeReductionPercentage(),
                'compression_ratio' => round($this->compressionRatio, 3),
                'compression_savings' => $this->getCompressionSavings(),
            ],
            'performance_metrics' => [
                'efficiency_score' => $this->calculateEfficiencyScore(),
                'quality_score' => $this->strategy->getQualityScore(),
                'performance_score' => $this->strategy->getPerformanceScore(),
                'overall_grade' => $this->getOverallGrade(),
            ],
            'applied_optimizations' => $this->optimizations,
            'metadata' => $this->metadata,
            'recommendations' => $this->generateRecommendations(),
            'data' => $this->optimizedData,
        ];
    }

    public function getSizeReduction(): int
    {
        return $this->originalSize - $this->optimizedSize;
    }

    public function getSizeReductionPercentage(): float
    {
        if ($this->originalSize === 0) {
            return 0.0;
        }

        return round((1 - $this->compressionRatio) * 100, 2);
    }

    public function getCompressionSavings(): string
    {
        $percentage = $this->getSizeReductionPercentage();
        $bytes = $this->getSizeReduction();

        return "{$percentage}% ({$this->formatBytes($bytes)} saved)";
    }

    public function calculateEfficiencyScore(): float
    {
        // Weight different factors for efficiency
        $sizeScore = $this->calculateSizeScore() * 0.4;      // 40% weight
        $qualityScore = $this->strategy->getQualityScore() * 0.3; // 30% weight
        $speedScore = $this->strategy->getPerformanceScore() * 0.3; // 30% weight

        return round($sizeScore + $qualityScore + $speedScore, 3);
    }

    public function getOverallGrade(): string
    {
        $score = $this->calculateEfficiencyScore();

        return match (true) {
            $score >= 0.95 => 'A+',
            $score >= 0.90 => 'A',
            $score >= 0.85 => 'B+',
            $score >= 0.80 => 'B',
            $score >= 0.75 => 'C+',
            $score >= 0.70 => 'C',
            $score >= 0.60 => 'D',
            default => 'F'
        };
    }

    public function isEffective(): bool
    {
        return $this->compressionRatio < 0.9 && // At least 10% reduction
               $this->calculateEfficiencyScore() >= 0.7; // Good efficiency
    }

    public function generateRecommendations(): array
    {
        $recommendations = [];

        // Size recommendations
        if ($this->compressionRatio > 0.9) {
            $recommendations[] = [
                'type' => 'compression',
                'priority' => 'medium',
                'message' => 'Consider using a more aggressive optimization strategy for better compression.',
                'current_reduction' => $this->getSizeReductionPercentage() . '%',
                'suggested_strategy' => OptimizationStrategy::AGGRESSIVE->value,
            ];
        }

        // Quality recommendations
        if ($this->strategy->getQualityScore() < 0.6) {
            $recommendations[] = [
                'type' => 'quality',
                'priority' => 'high',
                'message' => 'Current optimization may be too aggressive, consider balanced strategy.',
                'current_quality' => $this->strategy->getQualityScore(),
                'suggested_strategy' => OptimizationStrategy::BALANCED->value,
            ];
        }

        // Format-specific recommendations
        $formatRecommendations = $this->getFormatSpecificRecommendations();
        $recommendations = array_merge($recommendations, $formatRecommendations);

        // Performance recommendations
        if ($this->originalSize > 1024 * 1024 && $this->strategy === OptimizationStrategy::NONE) {
            $recommendations[] = [
                'type' => 'performance',
                'priority' => 'high',
                'message' => 'Large dataset detected without optimization. Enable optimization for better performance.',
                'current_size' => $this->formatBytes($this->originalSize),
                'suggested_strategy' => OptimizationStrategy::BALANCED->value,
            ];
        }

        return $recommendations;
    }

    public function getStatistics(): array
    {
        return [
            'size_metrics' => [
                'original_size' => $this->originalSize,
                'optimized_size' => $this->optimizedSize,
                'size_reduction' => $this->getSizeReduction(),
                'compression_ratio' => $this->compressionRatio,
            ],
            'optimization_metrics' => [
                'strategy_used' => $this->strategy->value,
                'optimizations_applied' => count($this->optimizations),
                'efficiency_score' => $this->calculateEfficiencyScore(),
                'quality_impact' => $this->calculateQualityImpact(),
            ],
            'format_metrics' => [
                'target_format' => $this->format->value,
                'format_compatibility' => $this->checkFormatCompatibility(),
                'format_efficiency' => $this->calculateFormatEfficiency(),
            ],
        ];
    }

    public function hasDataLoss(): bool
    {
        return in_array('data_truncation', $this->optimizations) ||
               in_array('array_truncation', $this->optimizations) ||
               $this->strategy === OptimizationStrategy::AGGRESSIVE;
    }

    public function getDataLossWarnings(): array
    {
        $warnings = [];

        if (in_array('data_truncation', $this->optimizations)) {
            $warnings[] = 'Some data has been truncated to meet size limits';
        }

        if (in_array('array_truncation', $this->optimizations)) {
            $warnings[] = 'Large arrays have been truncated';
        }

        if ($this->strategy === OptimizationStrategy::AGGRESSIVE) {
            $warnings[] = 'Aggressive optimization may have removed important details';
        }

        return $warnings;
    }

    private function calculateSizeScore(): float
    {
        // Score based on compression achieved
        $reductionPercentage = $this->getSizeReductionPercentage();

        return match (true) {
            $reductionPercentage >= 60 => 1.0,    // Excellent
            $reductionPercentage >= 40 => 0.8,    // Very good
            $reductionPercentage >= 25 => 0.6,    // Good
            $reductionPercentage >= 10 => 0.4,    // Fair
            default => 0.2                        // Poor
        };
    }

    private function calculateQualityImpact(): float
    {
        // Calculate how much quality was impacted by optimization
        $baseQuality = 1.0;
        $strategyImpact = 1.0 - $this->strategy->getQualityScore();

        return round($baseQuality - $strategyImpact, 3);
    }

    private function checkFormatCompatibility(): string
    {
        // Check if optimization is compatible with target format
        $compatibilityIssues = [];

        if ($this->format === OutputFormat::TABLE &&
            in_array('array_truncation', $this->optimizations)) {
            $compatibilityIssues[] = 'table_truncation';
        }

        if (($this->format === OutputFormat::MERMAID || $this->format === OutputFormat::PLANTUML) &&
            in_array('data_truncation', $this->optimizations)) {
            $compatibilityIssues[] = 'diagram_truncation';
        }

        return empty($compatibilityIssues) ? 'compatible' : 'issues_detected';
    }

    private function calculateFormatEfficiency(): float
    {
        // Calculate how well the optimization works with the target format
        $baseEfficiency = 0.8;

        // Bonus for format-optimized strategies
        if ($this->format === OutputFormat::JSON && $this->strategy === OptimizationStrategy::SIZE) {
            $baseEfficiency += 0.1;
        }

        if ($this->format === OutputFormat::TEXT && $this->strategy === OptimizationStrategy::SPEED) {
            $baseEfficiency += 0.1;
        }

        return min(1.0, $baseEfficiency);
    }

    private function getFormatSpecificRecommendations(): array
    {
        $recommendations = [];

        if ($this->format === OutputFormat::TABLE && $this->originalSize > 50 * 1024) {
            $recommendations[] = [
                'type' => 'format',
                'priority' => 'medium',
                'message' => 'Large datasets may not display well in table format. Consider tree or JSON format.',
                'suggested_format' => 'tree',
            ];
        }

        if (($this->format === OutputFormat::MERMAID || $this->format === OutputFormat::PLANTUML) &&
            is_array($this->optimizedData) && count($this->optimizedData) > 50) {
            $recommendations[] = [
                'type' => 'format',
                'priority' => 'high',
                'message' => 'Too many elements for diagram format. Consider using aggressive optimization.',
                'suggested_strategy' => OptimizationStrategy::AGGRESSIVE->value,
            ];
        }

        return $recommendations;
    }

    private function formatBytes(int $bytes): string
    {
        if ($bytes === 0) {
            return '0 B';
        }

        $units = ['B', 'KB', 'MB', 'GB'];
        $unitIndex = 0;
        $value = $bytes;

        while ($value >= 1024 && $unitIndex < count($units) - 1) {
            $value /= 1024;
            $unitIndex++;
        }

        return round($value, 2) . ' ' . $units[$unitIndex];
    }
}
