<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Core\Services;

use App\Framework\Mcp\Core\Interfaces\ResultFormatter;
use App\Framework\Mcp\Core\ValueObjects\OutputFormat;
use App\Framework\Mcp\Shared\Formatters\JsonFormatter;
use App\Framework\Mcp\Shared\Formatters\MermaidFormatter;
use App\Framework\Mcp\Shared\Formatters\OutputFormatter;
use App\Framework\Mcp\Shared\Formatters\PlantUmlFormatter;
use App\Framework\Mcp\Shared\Formatters\TableFormatter;
use App\Framework\Mcp\Shared\Formatters\TextFormatter;
use App\Framework\Mcp\Shared\Formatters\TreeFormatter;

/**
 * Haupt-Result-Formatter für MCP Tools
 *
 * Fungiert als Registry/Factory für alle spezialisierten Output-Formatter
 */
final readonly class McpResultFormatter implements ResultFormatter
{
    /** @var array<string, OutputFormatter> */
    private array $formatters;

    public function __construct()
    {
        // Register all available formatters
        $this->formatters = [
            OutputFormat::JSON->value => new JsonFormatter(),
            OutputFormat::TABLE->value => new TableFormatter(),
            OutputFormat::TREE->value => new TreeFormatter(),
            OutputFormat::TEXT->value => new TextFormatter(),
            OutputFormat::MERMAID->value => new MermaidFormatter(),
            OutputFormat::PLANTUML->value => new PlantUmlFormatter(),
        ];
    }

    public function format(mixed $data, string $format = 'array'): array
    {
        $outputFormat = OutputFormat::fromString($format);

        // Handle array format specially (no dedicated formatter needed)
        if ($outputFormat === OutputFormat::ARRAY) {
            return [
                'data' => is_array($data) ? $data : [$data],
                'format' => OutputFormat::ARRAY->value,
            ];
        }

        $formatter = $this->getFormatter($outputFormat);

        if (! $formatter) {
            throw new \InvalidArgumentException(
                "No formatter available for format: {$format}. " .
                "Supported formats: " . implode(', ', $this->getSupportedFormats())
            );
        }

        return $formatter->format($data);
    }

    public function formatAsTable(array $data, array $headers = []): array
    {
        $formatter = $this->getFormatter(OutputFormat::TABLE);

        if (! $formatter) {
            throw new \RuntimeException('Table formatter not available');
        }

        // Headers are handled internally by TableFormatter
        return $formatter->format($data);
    }

    public function formatAsTree(array $data, string $childKey = 'children'): array
    {
        // This would be implemented by a TreeFormatter
        // For now, return as nested structure
        return [
            'data' => $data,
            'format' => OutputFormat::TREE->value,
            'child_key' => $childKey,
        ];
    }

    public function formatAsMermaid(array $data, string $diagramType = 'flowchart'): string
    {
        // This would be implemented by a MermaidFormatter
        // For now, return basic flowchart
        return "graph TD\n    Start --> End";
    }

    public function formatAsPlantUml(array $data, string $diagramType = 'class'): string
    {
        // This would be implemented by a PlantUmlFormatter
        // For now, return basic class diagram
        return "@startuml\nclass Example\n@enduml";
    }

    public function supportsFormat(string $format): bool
    {
        $outputFormat = OutputFormat::fromString($format);

        return $this->hasFormatter($outputFormat);
    }

    public function getSupportedFormats(): array
    {
        return array_keys($this->formatters);
    }

    /**
     * Registriert einen neuen Formatter
     */
    public function registerFormatter(OutputFormatter $formatter): void
    {
        $this->formatters[$formatter->getFormat()->value] = $formatter;
    }

    /**
     * Holt einen Formatter für das angegebene Format
     */
    private function getFormatter(OutputFormat $format): ?OutputFormatter
    {
        return $this->formatters[$format->value] ?? null;
    }

    /**
     * Prüft ob ein Formatter für das Format verfügbar ist
     */
    private function hasFormatter(OutputFormat $format): bool
    {
        return isset($this->formatters[$format->value]);
    }

    /**
     * Gibt alle verfügbaren Formatter zurück
     */
    public function getAvailableFormatters(): array
    {
        return array_map(function (OutputFormatter $formatter) {
            return [
                'format' => $formatter->getFormat()->value,
                'description' => $formatter->getDescription(),
                'mime_type' => $formatter->getFormat()->getMimeType(),
                'file_extension' => $formatter->getFormat()->getFileExtension(),
                'is_diagram' => $formatter->getFormat()->isDiagram(),
                'is_structured' => $formatter->getFormat()->isStructured(),
            ];
        }, $this->formatters);
    }
}
