<?php

namespace App\Framework\SyntaxHighlighter;

use App\Framework\SyntaxHighlighter\Formatters\FormatterInterface;
use App\Framework\SyntaxHighlighter\Formatters\HtmlFormatter;
use App\Framework\SyntaxHighlighter\Formatters\ConsoleFormatter;

final readonly class SyntaxHighlighter {
    public Tokenizer $tokenizer;
    private array $formatters;

    public function __construct(
        public TokenMapper $tokenMapper = new TokenMapper(),
        ?Tokenizer $tokenizer = null
    ) {
        $this->tokenizer = $tokenizer ?? new Tokenizer($this->tokenMapper);
        $this->registerDefaultFormatters();
    }

    private function registerDefaultFormatters(): void {
        $formatters['html'] = new HtmlFormatter();
        #$this->formatters['console'] = new ConsoleFormatter();

        $this->formatters = $formatters;
    }

    public function highlight(string $code, string $format = 'html', array $options = []): string {
        if (!isset($this->formatters[$format])) {
            throw new \InvalidArgumentException("Unbekannter Formatter: {$format}");
        }

        $tokens = $this->tokenizer->tokenize($code);
        return $this->formatters[$format]->format($tokens, $options);
    }

    public function getFormatter(string $name): FormatterInterface {
        if (!isset($this->formatters[$name])) {
            throw new \InvalidArgumentException("Unbekannter Formatter: {$name}");
        }

        return $this->formatters[$name];
    }

    public function tokenize(string $code): array {
        return $this->tokenizer->tokenize($code);
    }

    public function addTokenMapping(int $phpToken, TokenType $tokenType): self {
        $this->tokenMapper->addMapping($phpToken, $tokenType);
        return $this;
    }

    public function setCustomTokenMappings(array $mappings): self {
        $this->tokenMapper->mergeMappings($mappings);
        return $this;
    }

    public function highlightWithCss(string $code, array $options = []): string {
        $options['includeCss'] = true;
        return $this->highlight($code, 'html', $options);
    }

    public function highlightWithoutCss(string $code, array $options = []): string {
        $options['includeCss'] = false;
        return $this->highlight($code, 'html', $options);
    }

    public function getCss(array $options = []): string {
        $formatter = $this->getFormatter('html');
        if (method_exists($formatter, 'getCss')) {
            return $formatter->getCss($options);
        }
        return '';
    }

    public function resetCssOutput(): self {
        if (method_exists($this->getFormatter('html'), 'resetCssOutput')) {
            $this->getFormatter('html')::resetCssOutput();
        }
        return $this;
    }

    public function highlightWithOffset(string $code, int $lineOffset, array $options = []): string {
        $options['lineOffset'] = $lineOffset;
        return $this->highlight($code, 'html', $options);
    }

    public function highlightFileSegment(string $code, int $startLineInFile, array $options = []): string {
        // startLineInFile ist die tatsächliche Zeilennummer in der Datei
        $options['lineOffset'] = $startLineInFile - 1;
        return $this->highlight($code, 'html', $options);
    }
}
