<?php

namespace App\Framework\SyntaxHighlighter\Formatters;

use App\Framework\SyntaxHighlighter\Token;
use App\Framework\SyntaxHighlighter\TokenType;

class HtmlFormatter implements FormatterInterface {
    private array $theme = [
        'keyword' => '#0000FF',
        'variable' => '#800080',
        'string' => '#008000',
        'identifier' => '#000000',
        'number' => '#FF0000',
        'comment' => '#808080',
        'doc-comment' => '#008080',
        'php-tag' => '#000080',
        'operator' => '#000080',
        'background' => '#F8F8F8',
        'border' => '#DDDDDD',
        'line-number' => '#999999',
    ];

    private static bool $cssOutputted = false;

    public function setTheme(array $theme): self {
        $this->theme = array_merge($this->theme, $theme);
        return $this;
    }

    public function format(array $tokens, array $options = []): string {
        $showLineNumbers = $options['lineNumbers'] ?? true;
        $startLine = $options['startLine'] ?? 1;
        $lineOffset = $options['lineOffset'] ?? 0; // Neuer Offset Parameter
        $highlightLines = $options['highlightLines'] ?? [];
        $cssClasses = $options['cssClasses'] ?? true;
        $includeCss = $options['includeCss'] ?? true;
        $language = $options['language'] ?? 'php';

        $lines = $this->groupTokensByLines($tokens);
        $output = [];

        foreach ($lines as $lineNumber => $lineTokens) {
            // Berücksichtigung von startLine und lineOffset
            $actualLineNumber = $lineNumber + $startLine - 1 + $lineOffset;
            $isHighlighted = in_array($actualLineNumber, $highlightLines);

            $lineContent = $this->formatLine($lineTokens, $cssClasses);
            $output[] = rtrim($this->wrapLine(
                $lineContent,
                $actualLineNumber,
                $showLineNumbers,
                $isHighlighted
            ));
        }

        $codeOutput = $this->wrapOutput(implode("\n", $output), $options);

        // CSS automatisch einbinden (nur einmal pro Request)
        if ($includeCss && !self::$cssOutputted) {
            $css = $this->generateCss($options);
            self::$cssOutputted = true;
            return $css . "\n" . $codeOutput;
        }

        return $codeOutput;
    }

    private function generateCss(array $options = []): string {
        $tabSize = $options['tabSize'] ?? 4;
        $fontSize = $options['fontSize'] ?? '14px';
        $lineHeight = $options['lineHeight'] ?? '1.5';

        return sprintf(
            '<style>%s</style>',
            $this->getCssRules($tabSize, $fontSize, $lineHeight)
        );
    }

    private function getCssRules(int $tabSize, string $fontSize, string $lineHeight): string {
        return <<<CSS
.syntax-highlighter {
    border-radius: 6px;
    overflow-x: auto;
    margin: 1em auto;
    position: relative;
    background-color: {$this->theme['background']};
    border: 1px solid {$this->theme['border']};
    max-width: fit-content;
    cursor: text;
}

.syntax-highlighter code {
    font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
    font-size: {$fontSize};
    line-height: {$lineHeight};
    padding: 16px;
    display: inline-flex;
    flex-direction: column;
    white-space: pre;
    tab-size: {$tabSize};
    color: inherit;
    background: none;
    margin: 0;
}

.syntax-highlighter .line {
    display: inline-flex;
    min-height: {$lineHeight}em;
    width: 100%;

    &:hover .line-number {
    font-weight: bold;
    }

    &:hover {
    background-color: rgba(255, 255, 0, 0.1);
    }
}

.syntax-highlighter .line.highlighted {
    background-color: rgba(255, 255, 0, 0.1);
}

.syntax-highlighter .line-number {
    padding-right: 16px;
    min-width: 40px;
    text-align: right;
    border-right: 1px solid {$this->theme['border']};
    margin-right: 16px;
    user-select: none;
    color: {$this->theme['line-number']};
}

.syntax-highlighter .code {
    flex: 1;
    padding-left: 8px;
}

/* Token Styles */
.syntax-highlighter .token-keyword {
    color: {$this->theme['keyword']};
    font-weight: bold;
}

.syntax-highlighter .token-variable {
    color: {$this->theme['variable']};
}

.syntax-highlighter .token-string {
    color: {$this->theme['string']};
}

.syntax-highlighter .token-comment {
    color: {$this->theme['comment']};
    font-style: italic;
}

.syntax-highlighter .token-doc-comment {
    color: {$this->theme['doc-comment']};
    font-style: italic;
}

.syntax-highlighter .token-number {
    color: {$this->theme['number']};
}

.syntax-highlighter .token-php-tag {
    color: {$this->theme['php-tag']};
    font-weight: bold;
}

.syntax-highlighter .token-operator {
    color: {$this->theme['operator']};
}

.syntax-highlighter .token-identifier {
    color: {$this->theme['identifier']};
}

/* Copy Button */
.syntax-highlighter .copy-btn {
    position: absolute;
    top: 8px;
    right: 8px;
    background: rgba(0, 0, 0, 0.8);
    color: white;
    border: none;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 12px;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.2s;
}

.syntax-highlighter:hover .copy-btn {
    opacity: 1;
}

.syntax-highlighter .copy-btn:hover {
    background: rgba(0, 0, 0, 0.9);
}
CSS;
    }

    public static function resetCssOutput(): void {
        self::$cssOutputted = false;
    }

    public function getCss(array $options = []): string {
        return $this->generateCss($options);
    }

    private function groupTokensByLines(array $tokens): array {
        $lines = [];
        $currentLine = 1;

        foreach ($tokens as $token) {
            if (!isset($lines[$currentLine])) {
                $lines[$currentLine] = [];
            }

            // Spezielle Behandlung für reine Newline-Token
            if ($token->value === "\n") {
                $currentLine++;
                continue;
            }

            if (str_contains($token->value, "\n")) {
                $parts = explode("\n", $token->value);

                // Ersten Teil zur aktuellen Zeile hinzufügen
                if ($parts[0] !== '') {
                    $lines[$currentLine][] = new Token(
                        $token->type,
                        $parts[0],
                        $currentLine,
                        $token->column
                    );
                }

                // Für jeden Newline eine neue Zeile erstellen
                for ($i = 1; $i < count($parts); $i++) {
                    $currentLine++;
                    if (!isset($lines[$currentLine])) {
                        $lines[$currentLine] = [];
                    }

                    // Nur nicht-leere Teile hinzufügen
                    if ($parts[$i] !== '') {
                        $lines[$currentLine][] = new Token(
                            $token->type,
                            $parts[$i],
                            $currentLine,
                            $token->column
                        );
                    }
                }
            } else {
                $lines[$currentLine][] = $token;
            }
        }

        return $lines;
    }

    private function formatLine(array $tokens, bool $useCssClasses): string {
        $content = '';

        foreach ($tokens as $token) {
            $content .= $this->formatToken($token, $useCssClasses);
        }

        return $content;
    }

    private function formatToken(Token $token, bool $useCssClasses): string {
        $escapedValue = htmlspecialchars($token->value);

        if ($token->type === TokenType::WHITESPACE) {
            return $escapedValue;
        }

        if ($useCssClasses) {
            return sprintf('<span class="%s">%s</span>',
                $token->type->getCssClass(), $escapedValue);
        } else {
            $color = $this->theme[$token->type->value] ?? $this->theme['identifier'];
            return sprintf('<span style="color: %s;">%s</span>',
                $color, $escapedValue);
        }
    }

    private function wrapLine(string $content, int $lineNumber, bool $showLineNumbers, bool $isHighlighted): string {
        $lineClass = $isHighlighted ? 'line highlighted' : 'line';

        if ($showLineNumbers) {
            $lineNumberSpan = sprintf(
                '<span class="line-number">%d</span>',
                $lineNumber
            );
            return sprintf('<div class="%s">%s<span class="code">%s</span></div>',
                $lineClass, $lineNumberSpan, $content);
        }

        return sprintf('<div class="%s">%s</div>', $lineClass, $content);
    }

    private function wrapOutput(string $content, array $options): string {
        $containerClass = $options['containerClass'] ?? 'syntax-highlighter';
        $language = $options['language'] ?? 'php';
        $copyButton = $options['copyButton'] ?? false;

        $copyButtonHtml = $copyButton ?
            '<button class="copy-btn" onclick="navigator.clipboard.writeText(this.nextElementSibling.querySelector(\'code\').textContent)">Copy</button>' : '';

        return sprintf(
            '<pre class="%s">%s<code class="language-%s">%s</code></pre>',
            $containerClass,
            $copyButtonHtml,
            $language,
            $content
        );
    }
}
