<?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;

/**
 * Console/Terminal formatter for the modern tokenizer
 */
final class ConsoleFormatter implements FormatterInterface
{
    /**
     * Format tokens for terminal output
     */
    public function format(TokenCollection|array $tokens, array $options = []): string
    {
        // Handle both old array format and new TokenCollection
        if (is_array($tokens)) {
            return $this->formatLegacy($tokens, $options);
        }

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

        $output = '';

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

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

            // Add line number if requested
            if ($lineNumbers) {
                $lineNumStr = sprintf("%4d | ", $displayLineNum);
                $output .= $colorize ? "\033[90m{$lineNumStr}\033[0m" : $lineNumStr;
            }

            // Highlight line background if needed
            if ($isHighlighted && $colorize) {
                $output .= "\033[43m"; // Yellow background
            }

            // Add tokens
            foreach ($lineTokens as $token) {
                $output .= $this->formatSingle($token, $options);
            }

            // Reset highlighting
            if ($isHighlighted && $colorize) {
                $output .= "\033[0m";
            }

            // Ensure line break
            if (! str_ends_with($output, "\n")) {
                $output .= "\n";
            }
        }

        return $output;
    }

    /**
     * Format a single token for terminal
     */
    public function formatSingle(Token $token, array $options = []): string
    {
        $colorize = $options['colorize'] ?? true;

        if (! $colorize) {
            return $token->value;
        }

        // Skip coloring for whitespace
        if ($token->type === TokenType::WHITESPACE) {
            return $token->value;
        }

        $color = $token->type->getTerminalColor();

        return $color . $token->value . "\033[0m";
    }

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

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

    /**
     * Legacy format method for old Token format
     */
    private function formatLegacy(array $tokens, array $options = []): string
    {
        $output = '';
        $colorize = $options['colorize'] ?? true;

        foreach ($tokens as $token) {
            if (is_object($token) && property_exists($token, 'value')) {
                if ($colorize && property_exists($token, 'type')) {
                    // Map old token type to color
                    $color = $this->getLegacyColor($token->type);
                    $output .= $color . $token->value . "\033[0m";
                } else {
                    $output .= $token->value;
                }
            }
        }

        return $output;
    }

    /**
     * Get color for legacy token type
     */
    private function getLegacyColor($tokenType): string
    {
        if (! is_object($tokenType) || ! property_exists($tokenType, 'name')) {
            return "\033[0m";
        }

        return match($tokenType->name) {
            'KEYWORD' => "\033[35m",
            'CLASS_NAME' => "\033[36m",
            'METHOD_CALL' => "\033[33m",
            'VARIABLE' => "\033[37m",
            'STRING' => "\033[32m",
            'NUMBER' => "\033[34m",
            'COMMENT', 'DOC_COMMENT' => "\033[90m",
            'OPERATOR' => "\033[31m",
            default => "\033[0m"
        };
    }
}
