<?php

declare(strict_types=1);

namespace App\Framework\Markdown;

final readonly class MarkdownConverter
{
    public function toHtml(string $markdown): string
    {
        $lines = explode("\n", $markdown);
        $html = [];
        $inCodeBlock = false;
        $inList = false;
        $listType = null;
        $currentParagraph = [];

        foreach ($lines as $line) {
            $trimmed = trim($line);

            // Code blocks
            if (str_starts_with($trimmed, '```')) {
                if ($inCodeBlock) {
                    $html[] = '</code></pre>';
                    $inCodeBlock = false;
                } else {
                    $this->flushParagraph($currentParagraph, $html);
                    $this->closeList($inList, $html);

                    $language = substr($trimmed, 3);
                    $langClass = $language ? " class=\"language-{$language}\"" : '';
                    $html[] = "<pre><code{$langClass}>";
                    $inCodeBlock = true;
                }

                continue;
            }

            if ($inCodeBlock) {
                $html[] = htmlspecialchars($line);

                continue;
            }

            // Empty line - close current paragraph/list
            if ($trimmed === '') {
                $this->flushParagraph($currentParagraph, $html);
                $this->closeList($inList, $html);

                continue;
            }

            // Headers
            if (preg_match('/^(#{1,6})\s+(.+)$/', $trimmed, $matches)) {
                $this->flushParagraph($currentParagraph, $html);
                $this->closeList($inList, $html);

                $level = strlen($matches[1]);
                $text = $this->parseInlineElements($matches[2]);
                $html[] = "<h{$level}>{$text}</h{$level}>";

                continue;
            }

            // Lists
            if (preg_match('/^[\-\*\+]\s+(.+)$/', $trimmed, $matches)) {
                $this->flushParagraph($currentParagraph, $html);
                $this->handleList($inList, $listType, 'ul', $html);

                $text = $this->parseInlineElements($matches[1]);
                $html[] = "<li>{$text}</li>";

                continue;
            }

            if (preg_match('/^\d+\.\s+(.+)$/', $trimmed, $matches)) {
                $this->flushParagraph($currentParagraph, $html);
                $this->handleList($inList, $listType, 'ol', $html);

                $text = $this->parseInlineElements($matches[1]);
                $html[] = "<li>{$text}</li>";

                continue;
            }

            // Blockquotes
            if (str_starts_with($trimmed, '> ')) {
                $this->flushParagraph($currentParagraph, $html);
                $this->closeList($inList, $html);

                $text = $this->parseInlineElements(substr($trimmed, 2));
                $html[] = "<blockquote>{$text}</blockquote>";

                continue;
            }

            // Horizontal rules
            if (preg_match('/^[\-\*_]{3,}$/', $trimmed)) {
                $this->flushParagraph($currentParagraph, $html);
                $this->closeList($inList, $html);
                $html[] = '<hr>';

                continue;
            }

            // Regular paragraph text
            $this->closeList($inList, $html);
            $currentParagraph[] = $this->parseInlineElements($trimmed);
        }

        // Flush remaining content
        $this->flushParagraph($currentParagraph, $html);
        $this->closeList($inList, $html);

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

    private function parseInlineElements(string $text): string
    {
        // Code spans (before other formatting)
        $text = preg_replace('/`([^`]+)`/', '<code>$1</code>', $text);

        // Bold and italic
        $text = preg_replace('/\*\*\*(.+?)\*\*\*/', '<strong><em>$1</em></strong>', $text);
        $text = preg_replace('/\*\*(.+?)\*\*/', '<strong>$1</strong>', $text);
        $text = preg_replace('/\*(.+?)\*/', '<em>$1</em>', $text);

        // Alternative bold/italic syntax
        $text = preg_replace('/___(.+?)___/', '<strong><em>$1</em></strong>', $text);
        $text = preg_replace('/__(.+?)__/', '<strong>$1</strong>', $text);
        $text = preg_replace('/_(.+?)_/', '<em>$1</em>', $text);

        // Links
        $text = preg_replace('/\[([^\]]+)\]\(([^)]+)\)/', '<a href="$2">$1</a>', $text);

        // Images
        $text = preg_replace('/!\[([^\]]*)\]\(([^)]+)\)/', '<img src="$2" alt="$1">', $text);

        return $text;
    }

    private function flushParagraph(array &$currentParagraph, array &$html): void
    {
        if (! empty($currentParagraph)) {
            $text = implode(' ', $currentParagraph);
            $html[] = "<p>{$text}</p>";
            $currentParagraph = [];
        }
    }

    private function handleList(bool &$inList, ?string &$listType, string $newType, array &$html): void
    {
        if (! $inList) {
            $html[] = "<{$newType}>";
            $inList = true;
            $listType = $newType;
        } elseif ($listType !== $newType) {
            $html[] = "</{$listType}>";
            $html[] = "<{$newType}>";
            $listType = $newType;
        }
    }

    private function closeList(bool &$inList, array &$html): void
    {
        if ($inList) {
            $html[] = "</{$this->getListType($html)}>";
            $inList = false;
        }
    }

    private function getListType(array $html): string
    {
        for ($i = count($html) - 1; $i >= 0; $i--) {
            if (str_starts_with($html[$i], '<ul>')) {
                return 'ul';
            }
            if (str_starts_with($html[$i], '<ol>')) {
                return 'ol';
            }
        }

        return 'ul';
    }

    public function stripMarkdown(string $markdown): string
    {
        $text = $markdown;

        // Entferne Markdown-Syntax
        $text = preg_replace('/^#+\s/', '', $text); // Headers
        $text = preg_replace('/\*\*(.+?)\*\*/', '$1', $text); // Bold
        $text = preg_replace('/\*(.+?)\*/', '$1', $text); // Italic
        $text = preg_replace('/```.*?\n(.*?)\n```/s', '$1', $text); // Code Blöcke
        $text = preg_replace('/`(.+?)`/', '$1', $text); // Inline Code
        $text = preg_replace('/\[(.+?)\]\((.+?)\)/', '$1 ($2)', $text); // Links
        $text = preg_replace('/^[\-\*] /', '', $text); // Listen
        $text = preg_replace('/^> /', '', $text); // Blockquotes

        return trim($text);
    }
}
