<?php

declare(strict_types=1);

namespace App\Framework\SyntaxHighlighter;

final readonly class DocBlockParser
{
    public function parseDocBlock(string $docBlock): array
    {
        $tokens = [];

        // Zeile für Zeile parsen, aber Originalformat beibehalten
        $lines = explode("\n", $docBlock);
        foreach ($lines as $lineIndex => $line) {
            if ($lineIndex === 0) {
                // Erste Zeile: /** plus Inhalt
                if (preg_match('/^(\/\*\*)(.*)$/', $line, $matches)) {
                    $tokens[] = ['type' => 'doc-comment', 'value' => $matches[1]]; // /**
                    if (! empty($matches[2])) { // Nicht trim() verwenden, um Leerzeichen zu erhalten
                        $contentTokens = $this->parseLineContent($matches[2]);
                        $tokens = array_merge($tokens, $contentTokens);
                    }
                }
            } elseif ($lineIndex === count($lines) - 1 && str_contains($line, '*/')) {
                // Letzte Zeile: Inhalt plus */
                if (preg_match('/^(.*)(\*\/)$/', $line, $matches)) {
                    $prefix = $matches[1];
                    if (preg_match('/^(\s*\*)(.*)$/', $prefix, $prefixMatches)) {
                        $tokens[] = ['type' => 'doc-comment', 'value' => $prefixMatches[1]]; // *
                        if (! empty($prefixMatches[2])) { // Leerzeichen beibehalten
                            $contentTokens = $this->parseLineContent($prefixMatches[2]);
                            $tokens = array_merge($tokens, $contentTokens);
                        }
                    }
                    $tokens[] = ['type' => 'doc-comment', 'value' => $matches[2]]; // */
                }
            } else {
                // Mittlere Zeilen: * plus Inhalt
                if (preg_match('/^(\s*\*)(.*)$/', $line, $matches)) {
                    $tokens[] = ['type' => 'doc-comment', 'value' => $matches[1]]; // *
                    if (! empty($matches[2])) { // Leerzeichen beibehalten
                        $contentTokens = $this->parseLineContent($matches[2]);
                        $tokens = array_merge($tokens, $contentTokens);
                    }
                }
            }

            // Newline hinzufügen (außer bei letzter Zeile)
            if ($lineIndex < count($lines) - 1) {
                $tokens[] = ['type' => 'doc-comment', 'value' => "\n"];
            }
        }

        return $tokens;
    }

    private function extractContent(string $docBlock): string
    {
        // /** und */ entfernen
        $content = preg_replace('/^\/\*\*/', '', $docBlock);
        $content = preg_replace('/\*\/$/', '', $content);

        // Führende * entfernen
        $lines = explode("\n", $content);
        $cleanLines = [];

        foreach ($lines as $line) {
            $line = ltrim($line);
            if (str_starts_with($line, '*')) {
                $line = ltrim(substr($line, 1));
            }
            $cleanLines[] = $line;
        }

        return implode("\n", $cleanLines);
    }

    private function parseLineContent(string $content): array
    {
        $tokens = [];

        // @tags erkennen (@var, @param, @return, etc.)
        if (preg_match('/@(\w+)/', $content, $matches, PREG_OFFSET_CAPTURE)) {
            $tagStart = $matches[0][1];
            $tag = $matches[1][0];

            // Text vor dem Tag
            if ($tagStart > 0) {
                $beforeTag = substr($content, 0, $tagStart);
                $tokens[] = ['type' => 'doc-text', 'value' => $beforeTag];
            }

            // Der Tag selbst
            $tokens[] = ['type' => 'doc-tag', 'value' => '@' . $tag];

            // Rest der Zeile nach dem Tag
            $afterTag = substr($content, $tagStart + strlen($matches[0][0]));
            if (! empty(trim($afterTag))) {
                $restTokens = $this->parseAfterTag($tag, trim($afterTag));
                $tokens = array_merge($tokens, $restTokens);
            }
        } else {
            // Normale Beschreibungszeile
            $tokens[] = ['type' => 'doc-text', 'value' => $content];
        }

        return $tokens;
    }

    private function parseAfterTag(string $tag, string $content): array
    {
        $tokens = [];

        switch ($tag) {
            case 'var':
            case 'param':
            case 'return':
                // Format: @var Type $variable description
                // oder: @param Type $variable description
                if (preg_match('/^(\S+)\s*(\$\w+)?\s*(.*)$/', $content, $matches)) {
                    // Typ
                    $tokens[] = ['type' => 'doc-type', 'value' => ' ' . $matches[1]];

                    // Variable (falls vorhanden)
                    if (! empty($matches[2])) {
                        $tokens[] = ['type' => 'doc-variable', 'value' => ' ' . $matches[2]];
                    }

                    // Beschreibung (falls vorhanden)
                    if (! empty(trim($matches[3]))) {
                        $tokens[] = ['type' => 'doc-text', 'value' => ' ' . $matches[3]];
                    }
                } else {
                    $tokens[] = ['type' => 'doc-text', 'value' => ' ' . $content];
                }

                break;

            case 'throws':
                // @throws ExceptionClass description
                if (preg_match('/^(\S+)\s*(.*)$/', $content, $matches)) {
                    $tokens[] = ['type' => 'doc-type', 'value' => ' ' . $matches[1]];
                    if (! empty(trim($matches[2]))) {
                        $tokens[] = ['type' => 'doc-text', 'value' => ' ' . $matches[2]];
                    }
                } else {
                    $tokens[] = ['type' => 'doc-text', 'value' => ' ' . $content];
                }

                break;

            default:
                // Andere Tags - einfach als Text behandeln
                $tokens[] = ['type' => 'doc-text', 'value' => ' ' . $content];

                break;
        }

        return $tokens;
    }
}
