<?php

declare(strict_types=1);

namespace App\Framework\Console;

use App\Framework\Discovery\Results\DiscoveryRegistry;

/**
 * Registry for command groups and workflows
 */
final readonly class CommandGroupRegistry
{
    private array $groups;

    private array $workflows;

    private array $commandGroupMapping;

    public function __construct(
        private DiscoveryRegistry $discoveryRegistry
    ) {
        $this->groups = $this->loadGroups();
        $this->workflows = $this->loadWorkflows();
        $this->commandGroupMapping = $this->buildCommandGroupMapping();
    }

    /**
     * Get all command groups
     */
    public function getGroups(): array
    {
        return $this->groups;
    }

    /**
     * Get group by name
     */
    public function getGroup(string $name): ?array
    {
        return $this->groups[$name] ?? null;
    }

    /**
     * Get all workflows
     */
    public function getWorkflows(): array
    {
        return $this->workflows;
    }

    /**
     * Get workflow by name
     */
    public function getWorkflow(string $name): ?array
    {
        return $this->workflows[$name] ?? null;
    }

    /**
     * Get command group mapping
     */
    public function getCommandGroupMapping(): array
    {
        return $this->commandGroupMapping;
    }

    /**
     * Get commands for a group
     */
    public function getCommandsForGroup(string $groupName): array
    {
        $commands = [];
        $allCommands = $this->discoveryRegistry->attributes()->get(ConsoleCommand::class);

        foreach ($allCommands as $commandData) {
            $commandGroup = $this->getCommandGroup($commandData);
            if ($commandGroup === $groupName) {
                $commands[] = $commandData;
            }
        }

        return $commands;
    }

    /**
     * Get organized command structure
     */
    public function getOrganizedCommands(): array
    {
        $organized = [];
        $allCommands = $this->discoveryRegistry->attributes()->get(ConsoleCommand::class);

        // Group commands by their groups
        foreach ($allCommands as $commandData) {
            $groupName = $this->getCommandGroup($commandData);
            $group = $this->getGroup($groupName);

            if (! isset($organized[$groupName])) {
                $organized[$groupName] = [
                    'name' => $groupName,
                    'description' => $group['description'] ?? '',
                    'icon' => $group['icon'] ?? $this->inferIcon($groupName),
                    'priority' => $group['priority'] ?? 0,
                    'commands' => [],
                ];
            }

            $organized[$groupName]['commands'][] = $commandData;
        }

        // Sort by priority
        uasort($organized, fn ($a, $b) => $b['priority'] <=> $a['priority']);

        // Convert to numeric array for TUI navigation (TuiState expects numeric indices)
        return array_values($organized);
    }

    /**
     * Check if command has explicit group
     */
    public function hasExplicitGroup(array $commandData): bool
    {
        return $this->getExplicitGroup($commandData) !== null;
    }

    /**
     * Load groups from discovery
     */
    private function loadGroups(): array
    {
        $groups = [];
        $results = $this->discoveryRegistry->attributes()->get(CommandGroup::class);

        foreach ($results as $result) {
            $group = $result->createAttributeInstance();
            if ($group === null) {
                continue;
            }
            $groups[$group->name] = [
                'name' => $group->name,
                'description' => $group->description,
                'icon' => $group->icon,
                'priority' => $group->priority,
                'dependencies' => $group->dependencies,
                'tags' => $group->tags,
                'hidden' => $group->hidden,
                'class' => $result->className->getFullyQualified(),
            ];
        }

        return $groups;
    }

    /**
     * Load workflows from discovery
     */
    private function loadWorkflows(): array
    {
        $workflows = [];
        $results = $this->discoveryRegistry->attributes()->get(CommandWorkflow::class);

        foreach ($results as $result) {
            $workflow = $result->createAttributeInstance();
            if ($workflow === null) {
                continue;
            }
            $workflows[$workflow->name] = [
                'name' => $workflow->name,
                'description' => $workflow->description,
                'steps' => array_map(
                    fn ($step) => is_array($step) ? WorkflowStep::fromArray($step) : $step,
                    $workflow->steps
                ),
                'prerequisites' => $workflow->prerequisites,
                'stopOnError' => $workflow->stopOnError,
                'rollbackSteps' => $workflow->rollbackSteps,
                'timeoutSeconds' => $workflow->timeoutSeconds,
                'environment' => $workflow->environment,
                'class' => $result->className->getFullyQualified(),
                'method' => $result->methodName?->toString(),
            ];
        }

        return $workflows;
    }

    /**
     * Build command to group mapping
     */
    private function buildCommandGroupMapping(): array
    {
        $mapping = [];
        $allCommands = $this->discoveryRegistry->attributes()->get(ConsoleCommand::class);

        foreach ($allCommands as $commandData) {
            $attribute = $commandData->createAttributeInstance();
            if ($attribute === null) {
                continue;
            }
            $commandName = $attribute->name;
            $groupName = $this->getCommandGroup($commandData);
            $mapping[$commandName] = $groupName;
        }

        return $mapping;
    }

    /**
     * Get command group (explicit or inferred)
     */
    private function getCommandGroup($commandData): string
    {
        // Check for explicit group attribute
        $explicitGroup = $this->getExplicitGroup($commandData);
        if ($explicitGroup !== null) {
            return $explicitGroup;
        }

        // Fall back to inference - get command name from attribute instance
        $attribute = $commandData->createAttributeInstance();
        if ($attribute === null) {
            return 'General';
        }

        return $this->inferCategory($attribute->name);
    }

    /**
     * Get explicit group from command
     */
    private function getExplicitGroup($commandData): ?string
    {
        $class = $commandData->className->getFullyQualified();
        $method = $commandData->methodName?->toString();

        // Check method-level group attribute
        if ($method) {
            $reflection = new \ReflectionMethod($class, $method);
            $attributes = $reflection->getAttributes(CommandGroup::class);
            if (! empty($attributes)) {
                return $attributes[0]->newInstance()->name;
            }
        }

        // Check class-level group attribute
        $reflection = new \ReflectionClass($class);
        $attributes = $reflection->getAttributes(CommandGroup::class);
        if (! empty($attributes)) {
            return $attributes[0]->newInstance()->name;
        }

        return null;
    }

    /**
     * Infer category from command name (fallback)
     */
    private function inferCategory(string $commandName): string
    {
        if (str_contains($commandName, ':')) {
            return ucfirst(explode(':', $commandName)[0]);
        }

        return match (true) {
            str_starts_with($commandName, 'test') => 'Testing',
            str_starts_with($commandName, 'demo') => 'Demo',
            str_starts_with($commandName, 'make') => 'Generator',
            str_starts_with($commandName, 'db') => 'Database',
            str_starts_with($commandName, 'mcp') => 'MCP',
            default => 'General'
        };
    }

    /**
     * Infer icon for group
     */
    private function inferIcon(string $groupName): string
    {
        return match (strtolower($groupName)) {
            'testing' => '🧪',
            'demo' => '🎮',
            'generator' => '⚙️',
            'database' => '🗄️',
            'mcp' => '🤖',
            'workflow' => '🔄',
            'system' => '⚙️',
            'security' => '🛡️',
            'general' => '📂',
            default => '📁'
        };
    }
}
