<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Shared\Formatters;

use App\Framework\Mcp\Core\ValueObjects\DiagramType;
use App\Framework\Mcp\Core\ValueObjects\OutputFormat;

/**
 * PlantUML Diagram Formatter
 *
 * Formatiert Daten als PlantUML-Diagramme für detaillierte technische Visualisierung
 */
final readonly class PlantUmlFormatter implements OutputFormatter
{
    public function format(mixed $data): array
    {
        $normalizedData = $this->normalizeToArray($data);

        if (empty($normalizedData)) {
            return [
                'content' => "@startuml\n!theme plain\ntitle Empty Data\nstart\n:No data available;\nstop\n@enduml",
                'format' => $this->getFormat()->value,
                'diagram_type' => DiagramType::PLANTUML_ACTIVITY->value,
                'element_count' => 1,
            ];
        }

        $diagramType = $this->detectPlantUmlType($normalizedData);
        $plantUmlCode = $this->generateDiagram($normalizedData, $diagramType);
        $stats = $this->analyzeDiagram($plantUmlCode);

        return [
            'content' => $plantUmlCode,
            'format' => $this->getFormat()->value,
            'diagram_type' => $diagramType->value,
            'diagram_description' => $diagramType->getDescription(),
            'complexity' => $diagramType->getComplexity(),
            'element_count' => $stats['elements'],
            'relationship_count' => $stats['relationships'],
        ];
    }

    public function supports(OutputFormat $format): bool
    {
        return $format === OutputFormat::PLANTUML;
    }

    public function getFormat(): OutputFormat
    {
        return OutputFormat::PLANTUML;
    }

    public function getDescription(): string
    {
        return $this->getFormat()->getDescription();
    }

    private function normalizeToArray(mixed $data): array
    {
        if (is_array($data)) {
            return $data;
        }

        if (is_object($data)) {
            return (array) $data;
        }

        return ['root' => $data];
    }

    private function detectPlantUmlType(array $data): DiagramType
    {
        $keys = array_keys($data);
        $keyString = implode(' ', $keys);

        // Check for specific PlantUML diagram patterns
        if (preg_match('/\b(class|method|property|inheritance|interface)\b/i', $keyString)) {
            return DiagramType::PLANTUML_CLASS;
        }

        if (preg_match('/\b(participant|message|sequence|interaction|call)\b/i', $keyString)) {
            return DiagramType::PLANTUML_SEQUENCE;
        }

        if (preg_match('/\b(actor|usecase|system|boundary)\b/i', $keyString)) {
            return DiagramType::PLANTUML_USE_CASE;
        }

        if (preg_match('/\b(activity|action|decision|fork|join)\b/i', $keyString)) {
            return DiagramType::PLANTUML_ACTIVITY;
        }

        if (preg_match('/\b(component|interface|package|node)\b/i', $keyString)) {
            return DiagramType::PLANTUML_COMPONENT;
        }

        if (preg_match('/\b(server|database|artifact|deployment)\b/i', $keyString)) {
            return DiagramType::PLANTUML_DEPLOYMENT;
        }

        // Default to class diagram for structured data
        return DiagramType::PLANTUML_CLASS;
    }

    private function generateDiagram(array $data, DiagramType $type): string
    {
        return match ($type) {
            DiagramType::PLANTUML_CLASS => $this->generateClassDiagram($data),
            DiagramType::PLANTUML_SEQUENCE => $this->generateSequenceDiagram($data),
            DiagramType::PLANTUML_USE_CASE => $this->generateUseCaseDiagram($data),
            DiagramType::PLANTUML_ACTIVITY => $this->generateActivityDiagram($data),
            DiagramType::PLANTUML_COMPONENT => $this->generateComponentDiagram($data),
            DiagramType::PLANTUML_DEPLOYMENT => $this->generateDeploymentDiagram($data),
            default => $this->generateClassDiagram($data)
        };
    }

    private function generateClassDiagram(array $data): string
    {
        $plantUml = "@startuml\n!theme plain\ntitle Class Diagram\n\n";

        foreach ($data as $className => $classData) {
            $sanitizedName = $this->sanitizeIdentifier($className);
            $plantUml .= "class {$sanitizedName} {\n";

            if (is_array($classData)) {
                // Add properties
                foreach ($classData as $key => $value) {
                    if (is_scalar($value)) {
                        $type = $this->getPlantUmlType($value);
                        $visibility = $this->getVisibility($key);
                        $plantUml .= "  {$visibility}{$key} : {$type}\n";
                    }
                }

                // Add separator for methods
                $plantUml .= "  --\n";

                // Add methods (arrays are treated as method signatures)
                foreach ($classData as $key => $value) {
                    if (is_array($value)) {
                        $returnType = isset($value['return']) ? $value['return'] : 'void';
                        $params = isset($value['params']) ? implode(', ', $value['params']) : '';
                        $visibility = $this->getVisibility($key);
                        $plantUml .= "  {$visibility}{$key}({$params}) : {$returnType}\n";
                    }
                }
            }

            $plantUml .= "}\n\n";
        }

        $plantUml .= "@enduml";

        return $plantUml;
    }

    private function generateSequenceDiagram(array $data): string
    {
        $plantUml = "@startuml\n!theme plain\ntitle Sequence Diagram\n\n";

        // Add participants
        if (isset($data['participants'])) {
            foreach ($data['participants'] as $participant) {
                $plantUml .= "participant \"{$participant}\" as {$this->sanitizeIdentifier($participant)}\n";
            }
            $plantUml .= "\n";
        }

        // Add messages
        if (isset($data['messages']) && is_array($data['messages'])) {
            foreach ($data['messages'] as $message) {
                if (is_array($message) && isset($message['from'], $message['to'], $message['text'])) {
                    $from = $this->sanitizeIdentifier($message['from']);
                    $to = $this->sanitizeIdentifier($message['to']);
                    $text = $message['text'];
                    $plantUml .= "{$from} -> {$to} : {$text}\n";
                }
            }
        } else {
            // Fallback: create simple sequence from array keys
            $participants = array_keys($data);
            for ($i = 0; $i < count($participants) - 1; $i++) {
                $from = $this->sanitizeIdentifier($participants[$i]);
                $to = $this->sanitizeIdentifier($participants[$i + 1]);
                $plantUml .= "{$from} -> {$to} : action\n";
            }
        }

        $plantUml .= "\n@enduml";

        return $plantUml;
    }

    private function generateUseCaseDiagram(array $data): string
    {
        $plantUml = "@startuml\n!theme plain\ntitle Use Case Diagram\n\n";

        // Add actors
        if (isset($data['actors'])) {
            foreach ($data['actors'] as $actor) {
                $plantUml .= "actor \"{$actor}\" as {$this->sanitizeIdentifier($actor)}\n";
            }
            $plantUml .= "\n";
        }

        // Add use cases
        if (isset($data['usecases'])) {
            foreach ($data['usecases'] as $usecase) {
                $sanitized = $this->sanitizeIdentifier($usecase);
                $plantUml .= "usecase \"{$usecase}\" as {$sanitized}\n";
            }
            $plantUml .= "\n";
        }

        // Add relationships
        if (isset($data['relationships'])) {
            foreach ($data['relationships'] as $rel) {
                if (is_array($rel) && isset($rel['from'], $rel['to'])) {
                    $from = $this->sanitizeIdentifier($rel['from']);
                    $to = $this->sanitizeIdentifier($rel['to']);
                    $plantUml .= "{$from} --> {$to}\n";
                }
            }
        }

        $plantUml .= "\n@enduml";

        return $plantUml;
    }

    private function generateActivityDiagram(array $data): string
    {
        $plantUml = "@startuml\n!theme plain\ntitle Activity Diagram\n\nstart\n\n";

        foreach ($data as $step => $details) {
            if (is_string($details)) {
                $plantUml .= ":{$details};\n";
            } elseif (is_array($details)) {
                if (isset($details['type']) && $details['type'] === 'decision') {
                    $question = $details['question'] ?? $step;
                    $plantUml .= "if ({$question}) then (yes)\n";
                    if (isset($details['yes'])) {
                        $plantUml .= "  :{$details['yes']};\n";
                    }
                    $plantUml .= "else (no)\n";
                    if (isset($details['no'])) {
                        $plantUml .= "  :{$details['no']};\n";
                    }
                    $plantUml .= "endif\n";
                } else {
                    $action = $details['action'] ?? $step;
                    $plantUml .= ":{$action};\n";
                }
            }
        }

        $plantUml .= "\nstop\n@enduml";

        return $plantUml;
    }

    private function generateComponentDiagram(array $data): string
    {
        $plantUml = "@startuml\n!theme plain\ntitle Component Diagram\n\n";

        foreach ($data as $component => $details) {
            $sanitized = $this->sanitizeIdentifier($component);

            if (is_array($details) && isset($details['type'])) {
                $type = $details['type'];
                $plantUml .= "{$type} \"{$component}\" as {$sanitized}\n";
            } else {
                $plantUml .= "component \"{$component}\" as {$sanitized}\n";
            }
        }

        $plantUml .= "\n";

        // Add relationships
        if (isset($data['relationships'])) {
            foreach ($data['relationships'] as $rel) {
                if (is_array($rel) && isset($rel['from'], $rel['to'])) {
                    $from = $this->sanitizeIdentifier($rel['from']);
                    $to = $this->sanitizeIdentifier($rel['to']);
                    $type = $rel['type'] ?? '-->';
                    $plantUml .= "{$from} {$type} {$to}\n";
                }
            }
        }

        $plantUml .= "\n@enduml";

        return $plantUml;
    }

    private function generateDeploymentDiagram(array $data): string
    {
        $plantUml = "@startuml\n!theme plain\ntitle Deployment Diagram\n\n";

        foreach ($data as $node => $details) {
            $sanitized = $this->sanitizeIdentifier($node);

            if (is_array($details)) {
                $type = $details['type'] ?? 'node';
                $plantUml .= "{$type} \"{$node}\" as {$sanitized} {\n";

                if (isset($details['artifacts'])) {
                    foreach ($details['artifacts'] as $artifact) {
                        $plantUml .= "  artifact \"{$artifact}\"\n";
                    }
                }

                $plantUml .= "}\n\n";
            } else {
                $plantUml .= "node \"{$node}\" as {$sanitized}\n";
            }
        }

        $plantUml .= "\n@enduml";

        return $plantUml;
    }

    private function sanitizeIdentifier(string $name): string
    {
        // PlantUML identifiers should not have spaces or special characters
        return preg_replace('/[^\w]/', '_', $name);
    }

    private function getPlantUmlType(mixed $value): string
    {
        return match (true) {
            is_bool($value) => 'boolean',
            is_int($value) => 'int',
            is_float($value) => 'double',
            is_string($value) => 'String',
            is_array($value) => 'List',
            default => 'Object'
        };
    }

    private function getVisibility(string $name): string
    {
        // Simple heuristic for visibility based on naming convention
        if (str_starts_with($name, '_')) {
            return '-'; // private
        }
        if (str_starts_with($name, 'protected')) {
            return '#'; // protected
        }

        return '+'; // public
    }

    private function analyzeDiagram(string $plantUmlCode): array
    {
        $elementCount = preg_match_all('/(class|component|actor|usecase|participant)/', $plantUmlCode, $matches);
        $relationshipCount = preg_match_all('/(-->|->|\.\.>|==)/', $plantUmlCode, $matches);

        return [
            'elements' => $elementCount,
            'relationships' => $relationshipCount,
        ];
    }
}
