<?php

declare(strict_types=1);

namespace App\Framework\Design\Analyzer;

use App\Framework\Core\ValueObjects\RGBColor;
use App\Framework\Design\ValueObjects\CssColor;
use App\Framework\Design\ValueObjects\CssParseResult;
use App\Framework\Design\ValueObjects\CssPropertyCategory;

/**
 * Analysiert Farben im Design System
 */
final readonly class ColorAnalyzer
{
    /**
     * Analysiert alle Farben im CSS
     */
    public function analyzeColors(CssParseResult $parseResult): ColorAnalysisResult
    {
        // Sammle alle Farben
        $colors = $this->collectAllColors($parseResult);

        if (empty($colors)) {
            return new ColorAnalysisResult(
                totalColors: 0,
                colorsByFormat: [],
                colorPalette: [],
                contrastAnalysis: [],
                duplicateColors: [],
                recommendations: ['No colors found in CSS. Consider implementing a color system with design tokens.']
            );
        }

        // Analysiere Farbpalette
        $palette = $this->generateColorPalette($colors);

        // Gruppiere nach Format
        $colorsByFormat = $this->groupColorsByFormat($colors);

        // Analysiere Kontraste (vereinfacht)
        $contrastAnalysis = $this->analyzeContrasts($colors);

        // Finde Duplikate
        $duplicates = $this->findDuplicateColors($colors);

        // Generiere Empfehlungen
        $recommendations = $this->generateRecommendations($colors, $palette, $duplicates, $colorsByFormat);

        return new ColorAnalysisResult(
            totalColors: count($colors),
            colorsByFormat: $colorsByFormat,
            colorPalette: $palette,
            contrastAnalysis: $contrastAnalysis,
            duplicateColors: $duplicates,
            recommendations: $recommendations
        );
    }

    /**
     * Analysiert spezifische Farbharmonien
     */
    public function analyzeColorHarmony(array $colors): array
    {
        $harmony = [
            'monochromatic' => [],
            'complementary' => [],
            'triadic' => [],
            'analogous' => [],
            'split_complementary' => [],
        ];

        foreach ($colors as $color) {
            $rgb = $color['color']->toRGB();
            if (! $rgb) {
                continue;
            }

            $hsl = $this->rgbToHsl($rgb);
            $hue = $hsl['h'];

            // Finde harmonische Farben
            foreach ($colors as $otherColor) {
                if ($color === $otherColor) {
                    continue;
                }

                $otherRgb = $otherColor['color']->toRGB();
                if (! $otherRgb) {
                    continue;
                }

                $otherHsl = $this->rgbToHsl($otherRgb);
                $otherHue = $otherHsl['h'];

                $hueDiff = abs($hue - $otherHue);
                $hueDiff = min($hueDiff, 360 - $hueDiff); // Zirkuläre Distanz

                // Klassifiziere Harmonie-Typ
                if ($hueDiff < 30) {
                    $harmony['analogous'][] = [$color, $otherColor];
                } elseif ($hueDiff > 150 && $hueDiff < 210) {
                    $harmony['complementary'][] = [$color, $otherColor];
                } elseif (abs($hueDiff - 120) < 30 || abs($hueDiff - 240) < 30) {
                    $harmony['triadic'][] = [$color, $otherColor];
                }
            }
        }

        return $harmony;
    }

    /**
     * Bewertet Farbzugänglichkeit
     */
    public function analyzeAccessibility(CssParseResult $parseResult): array
    {
        $accessibilityIssues = [];
        $colorProperties = $parseResult->getPropertiesByCategory(CssPropertyCategory::COLOR);

        // Sammle Text/Background-Kombinationen
        $textBackgroundPairs = [];

        foreach ($parseResult->rules as $rule) {
            $textColor = null;
            $backgroundColor = null;

            foreach ($rule->properties as $property) {
                if ($property->name === 'color') {
                    $textColor = $property->toColor();
                } elseif ($property->name === 'background-color') {
                    $backgroundColor = $property->toColor();
                }
            }

            if ($textColor && $backgroundColor) {
                $contrast = $this->calculateContrast($textColor, $backgroundColor);

                if ($contrast < 4.5) { // WCAG AA Standard
                    $accessibilityIssues[] = [
                        'type' => 'low_contrast',
                        'severity' => $contrast < 3.0 ? 'high' : 'medium',
                        'contrast_ratio' => round($contrast, 2),
                        'text_color' => $textColor->originalValue,
                        'background_color' => $backgroundColor->originalValue,
                        'selectors' => $rule->getSelectorStrings(),
                    ];
                }
            }
        }

        // Analysiere reine Farbkommunikation
        $colorOnlyIndicators = $this->findColorOnlyIndicators($parseResult);

        return [
            'contrast_issues' => $accessibilityIssues,
            'color_only_indicators' => $colorOnlyIndicators,
            'total_issues' => count($accessibilityIssues) + count($colorOnlyIndicators),
            'accessibility_score' => $this->calculateAccessibilityScore($accessibilityIssues, $colorOnlyIndicators),
        ];
    }

    /**
     * Sammelt alle Farben aus dem CSS
     */
    private function collectAllColors(CssParseResult $parseResult): array
    {
        $colors = [];

        // Farben aus Properties
        $colorProperties = $parseResult->getPropertiesByCategory(CssPropertyCategory::COLOR);

        foreach ($colorProperties as $property) {
            $color = $property->toColor();
            if ($color) {
                $colors[] = [
                    'color' => $color,
                    'property' => $property->name,
                    'context' => 'property',
                    'usage_count' => 1,
                ];
            }
        }

        // Farben aus Design Tokens
        foreach ($parseResult->customProperties as $token) {
            if ($token->hasValueType('color')) {
                $color = $token->getValueAs('color');
                if ($color instanceof CssColor) {
                    $colors[] = [
                        'color' => $color,
                        'property' => $token->name,
                        'context' => 'token',
                        'usage_count' => 0, // TODO: Track usage
                    ];
                }
            }
        }

        return $colors;
    }

    /**
     * Generiert eine strukturierte Farbpalette
     */
    private function generateColorPalette(array $colors): array
    {
        $palette = [
            'primary_colors' => [],
            'neutral_colors' => [],
            'accent_colors' => [],
            'semantic_colors' => [],
        ];

        foreach ($colors as $colorData) {
            $color = $colorData['color'];
            $property = $colorData['property'];

            // Kategorisiere Farben basierend auf Namen und Eigenschaften
            if ($this->isPrimaryColor($property, $color)) {
                $palette['primary_colors'][] = $colorData;
            } elseif ($this->isNeutralColor($property, $color)) {
                $palette['neutral_colors'][] = $colorData;
            } elseif ($this->isSemanticColor($property)) {
                $palette['semantic_colors'][] = $colorData;
            } else {
                $palette['accent_colors'][] = $colorData;
            }
        }

        // Sortiere jede Kategorie nach Helligkeit
        foreach ($palette as $category => &$categoryColors) {
            usort($categoryColors, function ($a, $b) {
                $rgbA = $a['color']->toRGB();
                $rgbB = $b['color']->toRGB();

                if (! $rgbA || ! $rgbB) {
                    return 0;
                }

                $lightnessA = $this->calculateLightness($rgbA);
                $lightnessB = $this->calculateLightness($rgbB);

                return $lightnessA <=> $lightnessB;
            });
        }

        return $palette;
    }

    /**
     * Gruppiert Farben nach Format
     */
    private function groupColorsByFormat(array $colors): array
    {
        $grouped = [];

        foreach ($colors as $colorData) {
            $format = $colorData['color']->format->value;

            if (! isset($grouped[$format])) {
                $grouped[$format] = [];
            }

            $grouped[$format][] = $colorData;
        }

        // Sortiere nach Häufigkeit
        uasort($grouped, fn ($a, $b) => count($b) <=> count($a));

        return $grouped;
    }

    /**
     * Analysiert Farbkontraste
     */
    private function analyzeContrasts(array $colors): array
    {
        $contrasts = [];

        // Erstelle Kontrast-Matrix für häufige Kombinationen
        foreach ($colors as $i => $colorA) {
            foreach ($colors as $j => $colorB) {
                if ($i >= $j) {
                    continue;
                }

                $contrast = $this->calculateContrast($colorA['color'], $colorB['color']);

                if ($contrast !== null) {
                    $contrasts[] = [
                        'color_a' => $colorA,
                        'color_b' => $colorB,
                        'contrast_ratio' => round($contrast, 2),
                        'wcag_aa' => $contrast >= 4.5,
                        'wcag_aaa' => $contrast >= 7.0,
                    ];
                }
            }
        }

        // Sortiere nach Kontrast-Ratio
        usort($contrasts, fn ($a, $b) => $b['contrast_ratio'] <=> $a['contrast_ratio']);

        return array_slice($contrasts, 0, 20); // Limitiere auf Top 20
    }

    /**
     * Findet doppelte Farben
     */
    private function findDuplicateColors(array $colors): array
    {
        $duplicates = [];
        $rgbMap = [];

        foreach ($colors as $colorData) {
            $rgb = $colorData['color']->toRGB();
            if (! $rgb) {
                continue;
            }

            $rgbKey = $rgb->red . ',' . $rgb->green . ',' . $rgb->blue;

            if (! isset($rgbMap[$rgbKey])) {
                $rgbMap[$rgbKey] = [];
            }

            $rgbMap[$rgbKey][] = $colorData;
        }

        // Finde Gruppen mit mehr als einem Eintrag
        foreach ($rgbMap as $rgbKey => $colorGroup) {
            if (count($colorGroup) > 1) {
                $duplicates[] = [
                    'rgb_values' => $rgbKey,
                    'colors' => $colorGroup,
                    'count' => count($colorGroup),
                    'potential_savings' => count($colorGroup) - 1,
                ];
            }
        }

        // Sortiere nach Anzahl der Duplikate
        usort($duplicates, fn ($a, $b) => $b['count'] <=> $a['count']);

        return $duplicates;
    }

    /**
     * Berechnet Kontrast zwischen zwei Farben
     */
    private function calculateContrast(CssColor $colorA, CssColor $colorB): ?float
    {
        $rgbA = $colorA->toRGB();
        $rgbB = $colorB->toRGB();

        if (! $rgbA || ! $rgbB) {
            return null;
        }

        $luminanceA = $this->calculateRelativeLuminance($rgbA);
        $luminanceB = $this->calculateRelativeLuminance($rgbB);

        $lighter = max($luminanceA, $luminanceB);
        $darker = min($luminanceA, $luminanceB);

        return ($lighter + 0.05) / ($darker + 0.05);
    }

    /**
     * Berechnet relative Luminanz
     */
    private function calculateRelativeLuminance(RGBColor $rgb): float
    {
        $r = $this->linearizeColorComponent($rgb->red / 255);
        $g = $this->linearizeColorComponent($rgb->green / 255);
        $b = $this->linearizeColorComponent($rgb->blue / 255);

        return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
    }

    /**
     * Linearisiert Farbkomponente für Luminanz-Berechnung
     */
    private function linearizeColorComponent(float $component): float
    {
        if ($component <= 0.03928) {
            return $component / 12.92;
        } else {
            return pow(($component + 0.055) / 1.055, 2.4);
        }
    }

    /**
     * Konvertiert RGB zu HSL
     * @return array{h: float, s: float, l: float}
     */
    private function rgbToHsl(RGBColor $rgb): array
    {
        $r = $rgb->red / 255;
        $g = $rgb->green / 255;
        $b = $rgb->blue / 255;

        $max = max($r, $g, $b);
        $min = min($r, $g, $b);
        $diff = $max - $min;

        // Lightness
        $l = ($max + $min) / 2;

        if ($diff === 0.0) {
            $h = $s = 0; // Achromatic
        } else {
            // Saturation
            $s = $l > 0.5 ? $diff / (2 - $max - $min) : $diff / ($max + $min);

            // Hue
            switch ($max) {
                case $r:
                    $h = (($g - $b) / $diff + ($g < $b ? 6 : 0)) / 6;

                    break;
                case $g:
                    $h = (($b - $r) / $diff + 2) / 6;

                    break;
                case $b:
                    $h = (($r - $g) / $diff + 4) / 6;

                    break;
                default:
                    $h = 0;
            }
        }

        return [
            'h' => $h * 360,
            's' => $s * 100,
            'l' => $l * 100,
        ];
    }

    /**
     * Berechnet Helligkeit einer Farbe
     */
    private function calculateLightness(RGBColor $rgb): float
    {
        return ($rgb->red * 0.299 + $rgb->green * 0.587 + $rgb->blue * 0.114) / 255;
    }

    /**
     * Prüft ob es eine primäre Farbe ist
     */
    private function isPrimaryColor(string $property, CssColor $color): bool
    {
        return str_contains(strtolower($property), 'primary') ||
               str_contains(strtolower($property), 'brand') ||
               str_contains(strtolower($property), 'main');
    }

    /**
     * Prüft ob es eine neutrale Farbe ist
     */
    private function isNeutralColor(string $property, CssColor $color): bool
    {
        if (str_contains(strtolower($property), 'gray') ||
            str_contains(strtolower($property), 'grey') ||
            str_contains(strtolower($property), 'neutral')) {
            return true;
        }

        // Prüfe RGB-Werte für Grautöne
        $rgb = $color->toRGB();
        if ($rgb) {
            $maxDiff = max($rgb->red, $rgb->green, $rgb->blue) - min($rgb->red, $rgb->green, $rgb->blue);

            return $maxDiff < 20; // Sehr niedrige Sättigung
        }

        return false;
    }

    /**
     * Prüft ob es eine semantische Farbe ist
     */
    private function isSemanticColor(string $property): bool
    {
        $semanticKeywords = ['success', 'error', 'warning', 'info', 'danger', 'alert'];

        foreach ($semanticKeywords as $keyword) {
            if (str_contains(strtolower($property), $keyword)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Findet reine Farbindikatoren (Accessibility Problem)
     */
    private function findColorOnlyIndicators(CssParseResult $parseResult): array
    {
        $indicators = [];

        // Suche nach Patterns die nur Farbe für Information nutzen
        foreach ($parseResult->rules as $rule) {
            $hasColorChange = false;
            $hasOtherIndicator = false;

            foreach ($rule->properties as $property) {
                if ($property->getCategory() === CssPropertyCategory::COLOR) {
                    $hasColorChange = true;
                }

                if (in_array($property->name, ['border', 'text-decoration', 'font-weight', 'background-image'])) {
                    $hasOtherIndicator = true;
                }
            }

            if ($hasColorChange && ! $hasOtherIndicator) {
                // Prüfe ob es sich um Status-Indikatoren handelt
                foreach ($rule->selectors as $selector) {
                    if (preg_match('/\.(error|success|warning|info|active|selected|invalid)/', $selector->value)) {
                        $indicators[] = [
                            'selector' => $selector->value,
                            'issue' => 'Uses color only for status indication',
                            'suggestion' => 'Add icons, borders, or text indicators alongside color',
                        ];
                    }
                }
            }
        }

        return $indicators;
    }

    /**
     * Berechnet Accessibility Score
     */
    private function calculateAccessibilityScore(array $contrastIssues, array $colorOnlyIndicators): int
    {
        $baseScore = 100;

        // Abzug für Kontrast-Probleme
        foreach ($contrastIssues as $issue) {
            $deduction = match($issue['severity']) {
                'high' => 15,
                'medium' => 10,
                default => 5
            };
            $baseScore -= $deduction;
        }

        // Abzug für reine Farbindikatoren
        $baseScore -= count($colorOnlyIndicators) * 5;

        return max(0, $baseScore);
    }

    /**
     * Generiert Empfehlungen
     */
    private function generateRecommendations(array $colors, array $palette, array $duplicates, array $colorsByFormat): array
    {
        $recommendations = [];

        // Duplikate-Empfehlungen
        if (count($duplicates) > 0) {
            $totalDuplicates = array_sum(array_column($duplicates, 'potential_savings'));
            $recommendations[] = "Remove {$totalDuplicates} duplicate colors to reduce CSS complexity and improve maintainability.";
        }

        // Format-Empfehlungen
        $formatCounts = array_map('count', $colorsByFormat);
        if (count($formatCounts) > 3) {
            $recommendations[] = 'Multiple color formats detected. Consider standardizing on fewer formats (e.g., hex for static colors, custom properties for theme colors).';
        }

        // OKLCH-Empfehlung
        if (! isset($colorsByFormat['oklch']) && count($colors) > 10) {
            $recommendations[] = 'Consider using OKLCH format for more perceptually uniform color interpolation and better color consistency.';
        }

        // Palette-Empfehlungen
        $totalColors = count($colors);
        if ($totalColors > 50) {
            $recommendations[] = "Large color palette detected ({$totalColors} colors). Consider consolidating similar colors into a smaller, more manageable palette.";
        }

        if (count($palette['primary_colors']) === 0 && $totalColors > 5) {
            $recommendations[] = 'No primary brand colors identified. Consider establishing clear primary colors for brand consistency.';
        }

        if (count($palette['neutral_colors']) < 3 && $totalColors > 10) {
            $recommendations[] = 'Limited neutral color palette. Consider adding more gray tones for better design flexibility.';
        }

        // Semantic Color Empfehlungen
        if (count($palette['semantic_colors']) === 0 && $totalColors > 8) {
            $recommendations[] = 'No semantic colors found. Consider adding success, error, warning, and info colors for UI feedback.';
        }

        if (empty($recommendations)) {
            $recommendations[] = 'Color system is well organized. Consider documenting color usage guidelines and accessibility standards.';
        }

        return $recommendations;
    }
}
