<?php

declare(strict_types=1);

namespace App\Framework\Design\Service;

use App\Framework\Design\Analyzer\ConventionCheckResult;

/**
 * Prüft Naming Conventions und Standards
 */
final readonly class ConventionChecker
{
    public function validateBemNaming(array $cssClasses): array
    {
        $valid = [];
        $invalid = [];

        foreach ($cssClasses as $cssClass) {
            $name = $cssClass->name;
            $isValid = true;
            $reason = '';

            // Check for invalid patterns
            if (preg_match('/^[A-Z]/', $name)) {
                $isValid = false;
                $reason = 'Should not start with uppercase';
            } elseif (str_contains($name, '__') && str_ends_with($name, '__')) {
                $isValid = false;
                $reason = 'Empty element name';
            } elseif (str_contains($name, '--') && str_ends_with($name, '--')) {
                $isValid = false;
                $reason = 'Empty modifier name';
            } elseif (preg_match('/--.*--/', $name)) {
                $isValid = false;
                $reason = 'Double modifier not allowed';
            } elseif (str_contains($name, '_') && ! str_contains($name, '__')) {
                $isValid = false;
                $reason = 'Use double underscore for elements';
            }

            if ($isValid) {
                $valid[] = $cssClass;
            } else {
                $invalid[] = [
                    'class' => $name,
                    'reason' => $reason,
                ];
            }
        }

        return [
            'valid' => $valid,
            'invalid' => $invalid,
        ];
    }

    public function validateKebabCase(array $cssClasses): array
    {
        $valid = [];
        $invalid = [];

        foreach ($cssClasses as $cssClass) {
            $name = $cssClass->name;

            if (preg_match('/^[a-z][a-z0-9-]*[a-z0-9]$/', $name)) {
                $valid[] = $cssClass;
            } else {
                $violation = $this->detectCaseViolation($name);
                $invalid[] = [
                    'class' => $name,
                    'violation' => $violation,
                ];
            }
        }

        return [
            'valid' => $valid,
            'invalid' => $invalid,
        ];
    }

    public function validateCustomPropertyNaming(array $customProperties): array
    {
        $valid = [];
        $invalid = [];

        foreach ($customProperties as $property) {
            $name = $property->name;

            if (preg_match('/^[a-z][a-z0-9-]*[a-z0-9]$/', $name)) {
                $valid[] = $property;
            } else {
                $invalid[] = [
                    'property' => $name,
                    'issue' => $this->detectPropertyIssue($name),
                ];
            }
        }

        return [
            'valid' => $valid,
            'invalid' => $invalid,
        ];
    }

    public function validateSemanticNaming(array $cssClasses): array
    {
        $semantic = [];
        $presentational = [];
        $positional = [];
        $generic = [];
        $vague = [];

        foreach ($cssClasses as $cssClass) {
            $name = $cssClass->name;
            $category = $this->categorizeSemanticNaming($name);

            match($category) {
                'semantic' => $semantic[] = $cssClass,
                'presentational' => $presentational[] = $cssClass,
                'positional' => $positional[] = $cssClass,
                'generic' => $generic[] = $cssClass,
                'vague' => $vague[] = $cssClass
            };
        }

        $total = count($cssClasses);
        $score = $total > 0 ? count($semantic) / $total : 0;

        return [
            'semantic' => $semantic,
            'presentational' => $presentational,
            'positional' => $positional,
            'generic' => $generic,
            'vague' => $vague,
            'score' => $score,
        ];
    }

    public function validateDesignTokenNaming(array $customProperties): array
    {
        $consistent = [];
        $inconsistent = [];

        foreach ($customProperties as $property) {
            $name = $property->name;
            $issue = $this->getTokenNamingIssue($name);

            if ($issue === null) {
                $consistent[] = $property;
            } else {
                $inconsistent[] = [
                    'property' => $name,
                    'issue' => $issue,
                ];
            }
        }

        return [
            'consistent' => $consistent,
            'inconsistent' => $inconsistent,
        ];
    }

    public function validateAccessibilityNaming(array $cssClasses): array
    {
        $accessibilityFriendly = [];
        $potentiallyProblematic = [];
        $recommendations = [];

        foreach ($cssClasses as $cssClass) {
            $name = $cssClass->name;

            if (in_array($name, ['sr-only', 'visually-hidden', 'skip-link', 'focus-visible'])) {
                $accessibilityFriendly[] = $cssClass;
            } elseif (in_array($name, ['hidden', 'invisible', 'no-display'])) {
                $potentiallyProblematic[] = $cssClass;

                if ($name === 'hidden') {
                    $recommendations[] = 'Consider "visually-hidden" instead of "hidden"';
                } elseif ($name === 'invisible') {
                    $recommendations[] = 'Consider "sr-only" instead of "invisible"';
                }
            }
        }

        return [
            'accessibility_friendly' => $accessibilityFriendly,
            'potentially_problematic' => $potentiallyProblematic,
            'recommendations' => array_unique($recommendations),
        ];
    }

    public function validateComponentHierarchy(array $cssClasses): array
    {
        $wellStructured = [];
        $poorlyStructured = [];
        $hierarchies = [];

        foreach ($cssClasses as $cssClass) {
            if ($cssClass->isBemBlock() || $cssClass->isBemElement()) {
                $wellStructured[] = $cssClass;

                $block = $cssClass->getBemBlock();
                if ($block) {
                    if (! isset($hierarchies[$block])) {
                        $hierarchies[$block] = ['elements' => [], 'depth' => 1];
                    }

                    if ($cssClass->isBemElement()) {
                        $element = $cssClass->getBemElement();
                        if ($element && ! in_array($element, $hierarchies[$block]['elements'])) {
                            $hierarchies[$block]['elements'][] = $element;
                            $hierarchies[$block]['depth'] = 2;
                        }
                    }
                }
            } else {
                $poorlyStructured[] = $cssClass;
            }
        }

        return [
            'well_structured' => $wellStructured,
            'poorly_structured' => $poorlyStructured,
            'hierarchies' => $hierarchies,
        ];
    }

    public function analyzeNamingConsistency(array $cssClasses): array
    {
        $patterns = [];
        $inconsistencies = [];

        foreach ($cssClasses as $cssClass) {
            $baseName = $this->getBaseName($cssClass->name);

            if (! isset($patterns[$baseName])) {
                $patterns[$baseName] = ['names' => [], 'consistency' => 1.0];
            }

            $patterns[$baseName]['names'][] = $cssClass->name;
        }

        // Check consistency within each pattern
        foreach ($patterns as $baseName => $data) {
            if (count($data['names']) > 1) {
                $uniquePatterns = array_unique(array_map([$this, 'getPattern'], $data['names']));
                if (count($uniquePatterns) > 1) {
                    $patterns[$baseName]['consistency'] = 1.0 / count($uniquePatterns);
                    $inconsistencies[] = "Mixed {$baseName} naming: " . implode(', ', $data['names']);
                }
            }
        }

        $overallScore = count($patterns) > 0 ?
            array_sum(array_column($patterns, 'consistency')) / count($patterns) : 1.0;

        return [
            'overall_score' => $overallScore,
            'patterns' => $patterns,
            'inconsistencies' => $inconsistencies,
        ];
    }

    public function suggestNamingImprovements(array $cssClasses): array
    {
        $suggestions = [];

        foreach ($cssClasses as $cssClass) {
            $name = $cssClass->name;
            $improved = $this->suggestImprovedName($name);

            if ($improved !== $name) {
                $suggestions[] = [
                    'original' => $name,
                    'improved' => $improved,
                    'reasons' => $this->getImprovementReasons($name, $improved),
                ];
            }
        }

        return $suggestions;
    }

    public function validateFrameworkConventions(array $cssClasses, string $framework): array
    {
        $compliant = [];
        $nonCompliant = [];

        foreach ($cssClasses as $cssClass) {
            $name = $cssClass->name;

            $isCompliant = match($framework) {
                'bootstrap' => $this->isBootstrapCompliant($name),
                'tailwind' => $this->isTailwindCompliant($name),
                'bem' => $this->isBemCompliant($name),
                default => true
            };

            if ($isCompliant) {
                $compliant[] = $cssClass;
            } else {
                $nonCompliant[] = $cssClass;
            }
        }

        return [
            'compliant' => $compliant,
            'non_compliant' => $nonCompliant,
        ];
    }

    private function detectCaseViolation(string $name): string
    {
        if (preg_match('/[a-z][A-Z]/', $name)) {
            return 'camelCase detected';
        } elseif (preg_match('/^[A-Z]/', $name)) {
            return 'PascalCase detected';
        } elseif (str_contains($name, '_')) {
            return 'snake_case detected';
        } elseif (ctype_upper($name)) {
            return 'SCREAMING_CASE detected';
        }

        return 'Invalid format';
    }

    private function detectPropertyIssue(string $name): string
    {
        if (preg_match('/[A-Z]/', $name)) {
            return 'Wrong case format';
        } elseif (preg_match('/^\d/', $name)) {
            return 'Cannot start with number';
        } elseif (str_starts_with($name, '--')) {
            return 'Should not include --';
        }

        return 'Invalid format';
    }

    private function categorizeSemanticNaming(string $name): string
    {
        if (in_array($name, ['header', 'navigation', 'content', 'sidebar', 'footer'])) {
            return 'semantic';
        } elseif (preg_match('/(red|blue|big|small|left|right)-/', $name)) {
            return str_contains($name, 'left') || str_contains($name, 'right') ? 'positional' : 'presentational';
        } elseif (preg_match('/^(div|thing)\d*$/', $name)) {
            return 'generic';
        } elseif ($name === 'thing') {
            return 'vague';
        }

        return 'semantic';
    }

    private function getTokenNamingIssue(string $name): ?string
    {
        if (strlen($name) < 3) {
            return 'Too generic';
        } elseif (strlen($name) > 50) {
            return 'Too verbose';
        } elseif (preg_match('/[A-Z]/', $name)) {
            return 'Wrong case format';
        }

        return null;
    }

    private function getBaseName(string $name): string
    {
        return explode('-', explode('_', $name)[0])[0];
    }

    private function getPattern(string $name): string
    {
        if (str_contains($name, '__') || str_contains($name, '--')) {
            return 'bem';
        } elseif (str_contains($name, '-')) {
            return 'kebab';
        } elseif (str_contains($name, '_')) {
            return 'snake';
        } elseif (preg_match('/[A-Z]/', $name)) {
            return 'camel';
        }

        return 'other';
    }

    private function suggestImprovedName(string $name): string
    {
        if ($name === 'redText') {
            return 'error-text';
        } elseif ($name === 'big_button') {
            return 'button--large';
        } elseif ($name === 'NAVIGATION') {
            return 'navigation';
        } elseif ($name === 'div123') {
            return 'content-section';
        } elseif ($name === 'thing') {
            return 'component';
        }

        return $name;
    }

    private function getImprovementReasons(string $original, string $improved): array
    {
        $reasons = [];

        if (preg_match('/[A-Z]/', $original)) {
            $reasons[] = 'Convert to kebab-case';
        }

        if (str_contains($original, '_') && ! str_contains($original, '__')) {
            $reasons[] = 'Convert to kebab-case';
        }

        if (preg_match('/(red|big)/', $original)) {
            $reasons[] = 'Use semantic naming';
        }

        if (str_contains($improved, '__') || str_contains($improved, '--')) {
            $reasons[] = 'Use BEM modifier pattern';
        }

        return $reasons;
    }

    /**
     * Main analysis method used by DesignSystemAnalyzer
     */
    public function checkConventions($parseResult): ConventionCheckResult
    {
        $bemValidation = $this->validateBemNaming($parseResult->classNames);
        $kebabValidation = $this->validateKebabCase($parseResult->classNames);
        $semanticValidation = $this->validateSemanticNaming($parseResult->classNames);
        $propertyValidation = $this->validateCustomPropertyNaming($parseResult->customProperties);
        $consistencyAnalysis = $this->analyzeNamingConsistency($parseResult->classNames);
        $accessibilityValidation = $this->validateAccessibilityNaming($parseResult->classNames);
        $hierarchyValidation = $this->validateComponentHierarchy($parseResult->classNames);

        $totalClasses = count($parseResult->classNames);
        $totalProperties = count($parseResult->customProperties);

        // Calculate overall score based on different validation results
        $scores = [
            'naming' => $totalClasses > 0 ? (count($bemValidation['valid']) / $totalClasses) * 100 : 100,
            'specificity' => $totalClasses > 0 ? (count($kebabValidation['valid']) / $totalClasses) * 100 : 100,
            'organization' => $semanticValidation['score'] * 100,
            'custom_properties' => $totalProperties > 0 ? (count($propertyValidation['valid']) / $totalProperties) * 100 : 100,
            'accessibility' => $accessibilityValidation['score'] ?? 100,
        ];

        $overallScore = (int) round(array_sum($scores) / count($scores));

        // Create violations array
        $violations = [];

        // Add critical violations
        foreach ($bemValidation['invalid'] as $invalid) {
            $violations[] = [
                'type' => 'naming',
                'severity' => 'high',
                'element' => $invalid['class'] ?? $invalid,
                'message' => $invalid['reason'] ?? 'BEM naming violation',
                'suggestion' => 'Use BEM methodology: block__element--modifier',
            ];
        }

        foreach ($propertyValidation['invalid'] as $invalid) {
            $violations[] = [
                'type' => 'custom_properties',
                'severity' => 'medium',
                'element' => $invalid['property'] ?? $invalid,
                'message' => $invalid['reason'] ?? 'Custom property naming violation',
                'suggestion' => 'Use kebab-case with semantic names',
            ];
        }

        // Add consistency violations
        foreach ($consistencyAnalysis['inconsistencies'] ?? [] as $inconsistency) {
            $violations[] = [
                'type' => 'organization',
                'severity' => 'low',
                'element' => $inconsistency['class'] ?? 'unknown',
                'message' => 'Naming inconsistency detected',
                'suggestion' => 'Standardize naming pattern',
            ];
        }

        $recommendations = [];
        if ($overallScore < 70) {
            $recommendations[] = 'Focus on improving naming conventions';
        }
        if ($scores['naming'] < 60) {
            $recommendations[] = 'Adopt BEM methodology for better component organization';
        }
        if ($scores['custom_properties'] < 80) {
            $recommendations[] = 'Improve custom property naming for better maintainability';
        }

        $conformanceLevel = match(true) {
            $overallScore >= 90 => 'excellent',
            $overallScore >= 80 => 'good',
            $overallScore >= 60 => 'fair',
            default => 'poor'
        };

        return new ConventionCheckResult(
            overallScore: $overallScore,
            categoryScores: $scores,
            violations: $violations,
            recommendations: $recommendations,
            conformanceLevel: $conformanceLevel
        );
    }

    private function isBootstrapCompliant(string $name): bool
    {
        $bootstrapPatterns = [
            'btn', 'btn-primary', 'btn-lg', 'container', 'row', 'col-md-6',
        ];

        return in_array($name, $bootstrapPatterns);
    }

    private function isTailwindCompliant(string $name): bool
    {
        return preg_match('/^(text-|bg-|p-|hover:|sm:)/', $name) === 1;
    }

    private function isBemCompliant(string $name): bool
    {
        return preg_match('/^[a-z][a-z0-9-]*(__[a-z][a-z0-9-]*)?(--[a-z][a-z0-9-]*)?$/', $name) === 1;
    }
}
