<?php

declare(strict_types=1);

namespace App\Framework\Design\Parser;

use App\Framework\Design\ValueObjects\CssProperty;
use App\Framework\Design\ValueObjects\CssRule;
use App\Framework\Design\ValueObjects\CssSelector;
use App\Framework\Filesystem\ValueObjects\FilePath;

/**
 * Parst CSS-Dateien und extrahiert Regeln, Selektoren und Properties
 */
final readonly class CssParser
{
    public function __construct(
        private CustomPropertyParser $customPropertyParser = new CustomPropertyParser(),
        private ClassNameParser $classNameParser = new ClassNameParser()
    ) {
    }

    /**
     * Parst eine CSS-Datei und gibt strukturierte Daten zurück
     */
    public function parseFile(FilePath $filePath): CssParseResult
    {
        if (! file_exists($filePath->toString())) {
            throw new \InvalidArgumentException("CSS file not found: {$filePath->toString()}");
        }

        $content = file_get_contents($filePath->toString());
        if ($content === false) {
            throw new \RuntimeException("Failed to read CSS file: {$filePath->toString()}");
        }

        return $this->parseContent($content, $filePath);
    }

    /**
     * Parst CSS-Content direkt
     */
    public function parseContent(string $content, ?FilePath $sourceFile = null): CssParseResult
    {
        // CSS Comments und unnötige Whitespaces entfernen
        $cleanContent = $this->cleanCssContent($content);

        // CSS Regeln extrahieren
        $rules = $this->extractRules($cleanContent);

        // Custom Properties extrahieren
        $customProperties = $this->customPropertyParser->extractFromContent($cleanContent);

        // CSS Klassen extrahieren
        $classNames = $this->classNameParser->extractFromContent($cleanContent);

        $statistics = [
            'total_rules' => count($rules),
            'total_custom_properties' => count($customProperties),
            'total_classes' => count($classNames),
        ];

        return new CssParseResult(
            sourceFile: $sourceFile,
            rules: $rules,
            customProperties: $customProperties,
            classNames: $classNames,
            rawContent: $content,
            statistics: $statistics
        );
    }

    /**
     * Parst mehrere CSS-Dateien
     * @param array<int, FilePath|string> $filePaths
     * @return array<int, CssParseResult>
     */
    public function parseFiles(array $filePaths): array
    {
        $results = [];

        foreach ($filePaths as $filePath) {
            $path = $filePath instanceof FilePath ? $filePath : FilePath::create($filePath);
            $results[] = $this->parseFile($path);
        }

        return $results;
    }

    /**
     * Parst alle CSS-Dateien in einem Verzeichnis
     * @return array<int, CssParseResult>
     */
    public function parseDirectory(string $directory, bool $recursive = true): array
    {
        $pattern = $recursive ? $directory . '/**/*.css' : $directory . '/*.css';
        $files = glob($pattern, GLOB_BRACE);

        if ($files === false) {
            return [];
        }

        return $this->parseFiles(array_map(fn ($file) => FilePath::create($file), $files));
    }

    /**
     * Extrahiert CSS-Regeln aus dem Content
     * @return array<int, CssRule>
     */
    private function extractRules(string $content): array
    {
        $rules = [];

        // Regex für CSS-Regeln: selector { properties }
        preg_match_all('/([^{}]+)\{([^{}]*)\}/s', $content, $matches, PREG_SET_ORDER);

        foreach ($matches as $match) {
            $selectorText = trim($match[1]);
            $propertiesText = trim($match[2]);

            // Skip rules without selectors, but allow empty properties
            if (empty($selectorText)) {
                continue;
            }

            // Selektoren parsen (können mehrere durch Komma getrennt sein)
            $selectors = $this->parseSelectors($selectorText);

            // Properties parsen
            $properties = $this->parseProperties($propertiesText);

            $rules[] = new CssRule($selectors, $properties);
        }

        return $rules;
    }

    /**
     * Parst Selektoren (durch Komma getrennt)
     * @return array<int, CssSelector>
     */
    private function parseSelectors(string $selectorText): array
    {
        $selectors = [];
        $selectorParts = explode(',', $selectorText);

        foreach ($selectorParts as $selector) {
            $selector = trim($selector);
            if (! empty($selector)) {
                $selectors[] = CssSelector::fromString($selector);
            }
        }

        return $selectors;
    }

    /**
     * Parst CSS Properties
     * @return array<int, CssProperty>
     */
    private function parseProperties(string $propertiesText): array
    {
        $properties = [];

        // Regex für property: value; Paare
        preg_match_all('/([^:;{}]+):\s*([^:;{}]+);?/s', $propertiesText, $matches, PREG_SET_ORDER);

        foreach ($matches as $match) {
            $property = trim($match[1]);
            $value = trim($match[2]);

            if (! empty($property) && ! empty($value)) {
                $properties[] = new CssProperty($property, $value);
            }
        }

        return $properties;
    }

    /**
     * Reinigt CSS Content von Comments und überflüssigen Whitespaces
     */
    private function cleanCssContent(string $content): string
    {
        // CSS Comments entfernen
        $content = preg_replace('/\/\*.*?\*\//s', '', $content);

        // Mehrfache Whitespaces reduzieren
        $content = preg_replace('/\s+/', ' ', $content);

        // Whitespace um geschweifte Klammern normalisieren
        $content = preg_replace('/\s*{\s*/', ' { ', $content);
        $content = preg_replace('/\s*}\s*/', ' } ', $content);

        return trim($content);
    }
}
