<?php

declare(strict_types=1);

namespace App\Framework\SyntaxHighlighter\Formatters;

use App\Framework\Tokenizer\ValueObjects\Token;
use App\Framework\Tokenizer\ValueObjects\TokenCollection;
use App\Framework\Tokenizer\ValueObjects\TokenType;

/**
 * HTML formatter for the modern tokenizer
 */
final class HtmlFormatter implements FormatterInterface
{
    private static bool $cssOutput = false;

    private array $customTheme = [];

    /**
     * Format tokens as HTML
     */
    public function format(TokenCollection|array $tokens, array $options = []): string
    {
        // Handle both old array format and new TokenCollection
        if (is_array($tokens)) {
            // Legacy support for old Token format
            return $this->formatLegacy($tokens, $options);
        }

        $includeCss = $options['includeCss'] ?? false;
        $lineNumbers = $options['lineNumbers'] ?? true;
        $lineOffset = $options['lineOffset'] ?? 0;
        $highlightLines = $options['highlightLines'] ?? [];
        $wrapInPre = $options['wrapInPre'] ?? true;

        $html = '';

        // Include CSS if requested and not already output
        if ($includeCss && ! self::$cssOutput) {
            $html .= $this->getCss($options);
            self::$cssOutput = true;
        }

        // Start wrapper
        if ($wrapInPre) {
            $html .= '<pre class="syntax-highlight"><code>';
        }

        // Group tokens by line for line number display
        $tokensByLine = $tokens->groupByLine();

        foreach ($tokensByLine as $lineNum => $lineTokens) {
            $displayLineNum = $lineNum + $lineOffset;
            $isHighlighted = in_array($displayLineNum, $highlightLines, true);

            // Add line wrapper
            $lineClass = $isHighlighted ? 'line highlighted' : 'line';
            $html .= sprintf('<div class="%s">', $lineClass);

            // Add line number
            if ($lineNumbers) {
                $html .= sprintf('<span class="line-number">%d</span>', $displayLineNum);
            }

            // Add tokens
            $html .= '<span class="code">';
            foreach ($lineTokens as $token) {
                $html .= $this->formatSingle($token, $options);
            }
            $html .= '</span></div>';
        }

        // End wrapper
        if ($wrapInPre) {
            $html .= '</code></pre>';
        }

        return $html;
    }

    /**
     * Format a single token
     */
    public function formatSingle(Token $token, array $options = []): string
    {
        // Skip whitespace tokens unless explicitly requested
        if ($token->type === TokenType::WHITESPACE && ! ($options['showWhitespace'] ?? false)) {
            return htmlspecialchars($token->value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        }

        $cssClass = $token->type->getCssClass();
        $escapedValue = htmlspecialchars($token->value, ENT_QUOTES | ENT_HTML5, 'UTF-8');

        return sprintf('<span class="%s">%s</span>', $cssClass, $escapedValue);
    }

    /**
     * Get CSS for syntax highlighting
     */
    public function getCss(array $options = []): string
    {
        // Use custom theme if set via setTheme()
        if (! empty($this->customTheme)) {
            return $this->generateCustomThemeCss($this->customTheme);
        }

        $theme = $options['theme'] ?? 'default';

        return match($theme) {
            'dark' => $this->getDarkThemeCss(),
            'light' => $this->getLightThemeCss(),
            default => $this->getDefaultThemeCss()
        };
    }

    /**
     * Get formatter name
     */
    public function getName(): string
    {
        return 'html';
    }

    /**
     * Check if formatter supports option
     */
    public function supportsOption(string $option): bool
    {
        return in_array($option, [
            'includeCss',
            'lineNumbers',
            'lineOffset',
            'highlightLines',
            'wrapInPre',
            'showWhitespace',
            'theme',
        ], true);
    }

    /**
     * Reset CSS output flag
     */
    public static function resetCssOutput(): void
    {
        self::$cssOutput = false;
    }

    /**
     * Set custom theme colors (backward compatibility)
     */
    public function setTheme(array $theme): self
    {
        $this->customTheme = $theme;

        return $this;
    }

    /**
     * Legacy format method for old Token format
     */
    private function formatLegacy(array $tokens, array $options = []): string
    {
        $html = '';
        foreach ($tokens as $token) {
            if (is_object($token) && property_exists($token, 'type') && property_exists($token, 'value')) {
                // Map old TokenType to CSS class
                $cssClass = 'token-' . str_replace('_', '-', strtolower($token->type->name));
                $escapedValue = htmlspecialchars($token->value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
                $html .= sprintf('<span class="%s">%s</span>', $cssClass, $escapedValue);
            }
        }

        return $html;
    }

    /**
     * Get default theme CSS
     */
    private function getDefaultThemeCss(): string
    {
        return <<<'CSS'
<style>
.syntax-highlight {
    background: #2d2d2d;
    color: #f8f8f2;
    padding: 1em;
    border-radius: 4px;
    overflow-x: auto;
    font-family: 'Fira Code', 'Consolas', monospace;
    font-size: 14px;
    line-height: 1.5;
}

.syntax-highlight .line {
    display: flex;
    min-height: 1.5em;
}

.syntax-highlight .line.highlighted {
    background: rgba(255, 255, 100, 0.1);
}

.syntax-highlight .line-number {
    color: #666;
    padding-right: 1em;
    user-select: none;
    min-width: 3em;
    text-align: right;
}

.syntax-highlight .code {
    flex: 1;
}

/* Token colors */
.token-keyword { color: #ff79c6; font-weight: bold; }
.token-class-name { color: #8be9fd; }
.token-interface-name { color: #8be9fd; }
.token-trait-name { color: #8be9fd; }
.token-enum-name { color: #8be9fd; }
.token-namespace-name { color: #50fa7b; }
.token-function-name { color: #f1fa8c; }
.token-method-name { color: #f1fa8c; }
.token-property-name { color: #bd93f9; }
.token-constant-name { color: #ff5555; }
.token-variable { color: #f8f8f2; }
.token-parameter { color: #ffb86c; }
.token-string-literal { color: #50fa7b; }
.token-number-literal { color: #bd93f9; }
.token-boolean-literal { color: #bd93f9; }
.token-null-literal { color: #bd93f9; }
.token-comment { color: #6272a4; font-style: italic; }
.token-doc-comment { color: #6272a4; font-style: italic; }
.token-doc-tag { color: #ff79c6; font-style: italic; }
.token-doc-type { color: #8be9fd; font-style: italic; }
.token-doc-variable { color: #ffb86c; font-style: italic; }
.token-operator { color: #ff79c6; }
.token-punctuation { color: #f8f8f2; }
.token-attribute { color: #ff79c6; }
.token-attribute-name { color: #f1fa8c; }
.token-type-hint { color: #8be9fd; }
.token-php-tag { color: #ff79c6; }
.token-error { color: #ff5555; background: rgba(255, 85, 85, 0.1); }
</style>
CSS;
    }

    /**
     * Get dark theme CSS
     */
    private function getDarkThemeCss(): string
    {
        return $this->getDefaultThemeCss(); // Dark is default
    }

    /**
     * Get light theme CSS
     */
    private function getLightThemeCss(): string
    {
        return <<<'CSS'
<style>
.syntax-highlight {
    background: #fafafa;
    color: #383a42;
    padding: 1em;
    border: 1px solid #e1e4e8;
    border-radius: 4px;
    overflow-x: auto;
    font-family: 'Fira Code', 'Consolas', monospace;
    font-size: 14px;
    line-height: 1.5;
}

.syntax-highlight .line {
    display: flex;
    min-height: 1.5em;
}

.syntax-highlight .line.highlighted {
    background: rgba(255, 251, 0, 0.2);
}

.syntax-highlight .line-number {
    color: #969896;
    padding-right: 1em;
    user-select: none;
    min-width: 3em;
    text-align: right;
}

/* Light theme token colors */
.token-keyword { color: #a626a4; font-weight: bold; }
.token-class-name { color: #c18401; }
.token-function-name { color: #4078f2; }
.token-method-name { color: #4078f2; }
.token-property-name { color: #986801; }
.token-constant-name { color: #986801; }
.token-variable { color: #383a42; }
.token-parameter { color: #e45649; }
.token-string-literal { color: #50a14f; }
.token-number-literal { color: #986801; }
.token-comment { color: #a0a1a7; font-style: italic; }
.token-operator { color: #a626a4; }
.token-attribute { color: #a626a4; }
.token-type-hint { color: #c18401; }
.token-error { color: #e45649; background: rgba(228, 86, 73, 0.1); }
</style>
CSS;
    }

    /**
     * Generate custom theme CSS from theme array
     */
    private function generateCustomThemeCss(array $theme): string
    {
        $background = $theme['background'] ?? '#2b2b2b';
        $color = $theme['identifier'] ?? '#a9b7c6';
        $border = $theme['border'] ?? '#555555';
        $lineNumber = $theme['line-number'] ?? '#606366';

        // Build CSS with custom colors
        $css = "<style>\n";
        $css .= ".syntax-highlight {\n";
        $css .= "    background: {$background};\n";
        $css .= "    color: {$color};\n";
        $css .= "    padding: 1em;\n";
        $css .= "    border: 1px solid {$border};\n";
        $css .= "    border-radius: 4px;\n";
        $css .= "    overflow-x: auto;\n";
        $css .= "    font-family: 'Fira Code', 'Consolas', monospace;\n";
        $css .= "    font-size: 14px;\n";
        $css .= "    line-height: 1.5;\n";
        $css .= "}\n\n";

        $css .= ".syntax-highlight .line { display: flex; min-height: 1.5em; }\n";
        $css .= ".syntax-highlight .line.highlighted { background: rgba(255, 255, 100, 0.1); }\n";
        $css .= ".syntax-highlight .line-number {\n";
        $css .= "    color: {$lineNumber};\n";
        $css .= "    padding-right: 1em;\n";
        $css .= "    user-select: none;\n";
        $css .= "    min-width: 3em;\n";
        $css .= "    text-align: right;\n";
        $css .= "}\n";
        $css .= ".syntax-highlight .code { flex: 1; }\n\n";

        // Map theme keys to CSS token classes
        $tokenMapping = [
            'keyword' => 'token-keyword',
            'variable' => 'token-variable',
            'string' => 'token-string-literal',
            'number' => 'token-number-literal',
            'comment' => 'token-comment',
            'doc-comment' => 'token-doc-comment',
            'php-tag' => 'token-php-tag',
            'operator' => 'token-operator',
            'constant' => 'token-constant-name',
            'method-call' => 'token-method-name',
            'property' => 'token-property-name',
            'class-name' => 'token-class-name',
        ];

        // Generate token-specific styles
        foreach ($tokenMapping as $themeKey => $cssClass) {
            if (isset($theme[$themeKey])) {
                $css .= ".{$cssClass} { color: {$theme[$themeKey]}; }\n";
            }
        }

        // Special cases with additional styling
        if (isset($theme['keyword'])) {
            $css .= ".token-keyword { color: {$theme['keyword']}; font-weight: bold; }\n";
        }
        if (isset($theme['comment'])) {
            $css .= ".token-comment { color: {$theme['comment']}; font-style: italic; }\n";
        }
        if (isset($theme['doc-comment'])) {
            $css .= ".token-doc-comment { color: {$theme['doc-comment']}; font-style: italic; }\n";
        }

        $css .= "</style>";

        return $css;
    }
}
