<?php

declare(strict_types=1);

namespace App\Framework\Design\Analyzer;

use App\Framework\Design\ValueObjects\CssParseResult;
use App\Framework\Design\ValueObjects\DesignToken;
use App\Framework\Design\ValueObjects\DesignTokenType;

/**
 * Analyzer für Design Tokens
 */
final readonly class TokenAnalyzer
{
    /**
     * Analysiert Design Tokens im CSS
     */
    public function analyze(CssParseResult $parseResult): TokenAnalysisResult
    {
        $tokens = $parseResult->customProperties;

        if (empty($tokens)) {
            return new TokenAnalysisResult(
                totalTokens: 0,
                tokensByType: [],
                tokenHierarchy: [],
                unusedTokens: [],
                missingTokens: [],
                tokenUsage: [],
                recommendations: ['No design tokens found. Consider implementing CSS custom properties for better maintainability.']
            );
        }

        // Analysiere Token-Typen
        $tokensByType = $this->groupTokensByType($tokens);

        // Analysiere Token-Hierarchie
        $tokenHierarchy = $this->analyzeTokenHierarchy($tokens);

        // Analysiere Token-Usage
        $tokenUsage = $this->analyzeTokenUsage($parseResult, $tokens);

        // Finde unbenutzte Tokens
        $unusedTokens = $this->findUnusedTokens($tokens, $tokenUsage);

        // Finde verwendete Tokens (alle Tokens minus unbenutzte)
        $usedTokens = array_filter($tokens, fn ($token) => ! in_array($token, $unusedTokens, true));

        // Finde fehlende Standard-Tokens
        $missingTokens = $this->findMissingStandardTokens($tokensByType);

        // Generiere Empfehlungen
        $recommendations = $this->generateRecommendations($tokensByType, $unusedTokens, $missingTokens);

        return new TokenAnalysisResult(
            totalTokens: count($tokens),
            tokensByType: $tokensByType,
            tokenHierarchy: $tokenHierarchy,
            unusedTokens: $unusedTokens,
            missingTokens: $missingTokens,
            tokenUsage: $tokenUsage,
            recommendations: $recommendations,
            usedTokens: $usedTokens
        );
    }

    /**
     * Gruppiert Tokens nach Typ
     */
    private function groupTokensByType(array $tokens): array
    {
        $grouped = [];

        foreach ($tokens as $token) {
            $type = $token->type->value;

            if (! isset($grouped[$type])) {
                $grouped[$type] = [
                    'count' => 0,
                    'tokens' => [],
                ];
            }

            $grouped[$type]['count']++;
            $grouped[$type]['tokens'][] = $token;
        }

        return $grouped;
    }

    /**
     * Analysiert Token-Hierarchie (basierend auf Naming)
     */
    private function analyzeTokenHierarchy(array $tokens): array
    {
        $hierarchy = [];

        foreach ($tokens as $token) {
            $nameParts = explode('-', $token->name);
            $current = &$hierarchy;

            foreach ($nameParts as $part) {
                if (! isset($current[$part])) {
                    $current[$part] = [
                        'tokens' => [],
                        'children' => [],
                    ];
                }
                $current = &$current[$part]['children'];
            }

            // Token zur letzten Ebene hinzufügen
            $lastPart = end($nameParts);
            if (isset($hierarchy[$lastPart])) {
                $hierarchy[$lastPart]['tokens'][] = $token;
            }
        }

        return $this->cleanupHierarchy($hierarchy);
    }

    /**
     * Analysiert Token-Usage in CSS Rules
     */
    private function analyzeTokenUsage(CssParseResult $parseResult, array $tokens): array
    {
        $usage = [];
        $tokenNames = array_map(fn (DesignToken $token) => $token->name, $tokens);

        foreach ($parseResult->rules as $rule) {
            foreach ($rule->properties as $property) {
                if ($property->usesCustomProperty()) {
                    $refs = $property->getCustomPropertyReferences();

                    foreach ($refs as $ref) {
                        if (in_array($ref, $tokenNames)) {
                            if (! isset($usage[$ref])) {
                                $usage[$ref] = [
                                    'count' => 0,
                                    'properties' => [],
                                    'selectors' => [],
                                ];
                            }

                            $usage[$ref]['count']++;
                            $usage[$ref]['properties'][] = $property->name;
                            $usage[$ref]['selectors'] = array_merge(
                                $usage[$ref]['selectors'],
                                $rule->getSelectorStrings()
                            );
                        }
                    }
                }
            }
        }

        // Deduplicate arrays
        foreach ($usage as $tokenName => $data) {
            $usage[$tokenName]['properties'] = array_unique($data['properties']);
            $usage[$tokenName]['selectors'] = array_unique($data['selectors']);
        }

        return $usage;
    }

    /**
     * Findet unbenutzte Tokens
     */
    private function findUnusedTokens(array $tokens, array $usage): array
    {
        $unused = [];

        foreach ($tokens as $token) {
            if (! isset($usage[$token->name])) {
                $unused[] = $token;
            }
        }

        return $unused;
    }

    /**
     * Findet fehlende Standard-Tokens
     */
    private function findMissingStandardTokens(array $tokensByType): array
    {
        $standardTokens = [
            DesignTokenType::COLOR->value => [
                'primary', 'secondary', 'success', 'warning', 'error', 'info',
                'background', 'surface', 'text-primary', 'text-secondary',
            ],
            DesignTokenType::SPACING->value => [
                'xs', 'sm', 'md', 'lg', 'xl', '2xl',
                'gap-xs', 'gap-sm', 'gap-md', 'gap-lg',
            ],
            DesignTokenType::TYPOGRAPHY->value => [
                'font-primary', 'font-secondary',
                'text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl',
                'weight-normal', 'weight-bold',
            ],
            DesignTokenType::RADIUS->value => [
                'radius-sm', 'radius-md', 'radius-lg', 'radius-full',
            ],
            DesignTokenType::SHADOW->value => [
                'shadow-sm', 'shadow-md', 'shadow-lg',
            ],
        ];

        $missing = [];

        foreach ($standardTokens as $type => $expectedTokens) {
            if (! isset($tokensByType[$type])) {
                $missing[$type] = $expectedTokens;

                continue;
            }

            $existingNames = array_map(
                fn (DesignToken $token) => $token->name,
                $tokensByType[$type]['tokens']
            );

            $missingInType = array_diff($expectedTokens, $existingNames);

            if (! empty($missingInType)) {
                $missing[$type] = $missingInType;
            }
        }

        return $missing;
    }

    /**
     * Generiert Empfehlungen basierend auf der Analyse
     */
    private function generateRecommendations(array $tokensByType, array $unusedTokens, array $missingTokens): array
    {
        $recommendations = [];

        // Empfehlungen für unbenutzte Tokens
        if (! empty($unusedTokens)) {
            $recommendations[] = sprintf(
                'Remove %d unused design tokens to reduce CSS complexity.',
                count($unusedTokens)
            );
        }

        // Empfehlungen für fehlende Tokens
        if (! empty($missingTokens)) {
            foreach ($missingTokens as $type => $tokens) {
                $recommendations[] = sprintf(
                    'Consider adding %s tokens: %s',
                    $type,
                    implode(', ', array_slice($tokens, 0, 3)) . (count($tokens) > 3 ? '...' : '')
                );
            }
        }

        // Empfehlungen für Token-Balance
        $totalTokens = array_sum(array_column($tokensByType, 'count'));
        if ($totalTokens > 0) {
            foreach ($tokensByType as $type => $data) {
                $percentage = round(($data['count'] / $totalTokens) * 100, 1);

                if ($type === 'color' && $percentage > 50) {
                    $recommendations[] = 'High percentage of color tokens detected. Consider consolidating similar colors.';
                }

                if ($type === 'spacing' && $percentage < 10 && $totalTokens > 10) {
                    $recommendations[] = 'Low percentage of spacing tokens. Consider adding more consistent spacing values.';
                }
            }
        }

        // Empfehlungen für Naming Conventions
        if (isset($tokensByType[DesignTokenType::COLOR->value])) {
            $colorTokens = $tokensByType[DesignTokenType::COLOR->value]['tokens'];
            $semanticNames = 0;
            $literalNames = 0;

            foreach ($colorTokens as $token) {
                if (preg_match('/^(primary|secondary|success|warning|error|info|background|surface|text)/', $token->name)) {
                    $semanticNames++;
                } elseif (preg_match('/^(red|blue|green|yellow|orange|purple|gray|black|white)/', $token->name)) {
                    $literalNames++;
                }
            }

            if ($literalNames > $semanticNames) {
                $recommendations[] = 'Consider using more semantic color names (primary, secondary) instead of literal names (red, blue).';
            }
        }

        if (empty($recommendations)) {
            $recommendations[] = 'Design token structure looks good! Consider documenting token usage guidelines.';
        }

        return $recommendations;
    }

    /**
     * Bereinigt die Hierarchie von leeren Einträgen
     */
    private function cleanupHierarchy(array $hierarchy): array
    {
        $cleaned = [];

        foreach ($hierarchy as $key => $value) {
            if (! empty($value['tokens']) || ! empty($value['children'])) {
                $cleaned[$key] = [
                    'tokens' => $value['tokens'],
                    'children' => $this->cleanupHierarchy($value['children']),
                ];
            }
        }

        return $cleaned;
    }
}
