<?php

declare(strict_types=1);

namespace App\Framework\Console;

use ReflectionClass;
use ReflectionException;
use ReflectionMethod;

/**
 * Generates comprehensive help text from command method signatures
 */
final readonly class CommandHelpGenerator
{
    public function __construct(
        private readonly ParameterInspector $parameterInspector
    ) {
    }

    /**
     * Generate comprehensive help for a command
     */
    public function generateHelp(object $command, string $methodName = '__invoke'): CommandHelp
    {
        try {
            $reflection = new ReflectionMethod($command, $methodName);
            $classReflection = new ReflectionClass($command);

            $commandInfo = $this->extractCommandInfo($reflection);
            $parameters = $this->parameterInspector->inspectCommand($command, $methodName);
            $usage = $this->generateUsageExample($commandInfo['name'], $parameters);
            $description = $this->generateDescription($commandInfo, $classReflection);
            $examples = $this->generateExamples($commandInfo['name'], $parameters);

            return new CommandHelp(
                name: $commandInfo['name'],
                description: $description,
                usage: $usage,
                parameters: $parameters,
                examples: $examples,
                className: $classReflection->getName(),
                methodName: $methodName,
                aliases: $commandInfo['aliases'] ?? []
            );
        } catch (ReflectionException $e) {
            return CommandHelp::empty($command::class);
        }
    }

    /**
     * Extract command information from ConsoleCommand attribute
     */
    private function extractCommandInfo(ReflectionMethod $method): array
    {
        $attributes = $method->getAttributes();

        foreach ($attributes as $attribute) {
            if ($attribute->getName() === ConsoleCommand::class) {
                $args = $attribute->getArguments();

                return [
                    'name' => $args[0] ?? $args['name'] ?? 'unknown',
                    'description' => $args[1] ?? $args['description'] ?? '',
                    'aliases' => $args['aliases'] ?? [],
                ];
            }
        }

        return [
            'name' => 'unknown',
            'description' => '',
            'aliases' => [],
        ];
    }

    /**
     * Generate enhanced description with parameter analysis
     */
    private function generateDescription(array $commandInfo, ReflectionClass $class): string
    {
        $description = $commandInfo['description'];

        if (empty($description)) {
            // Generate description from class name and method analysis
            $className = $class->getShortName();
            $cleanName = preg_replace('/Command$/', '', $className);
            $description = "Execute {$cleanName} operation";
        }

        // Add class information
        $classDoc = $class->getDocComment();
        if ($classDoc && preg_match('/\*\s*(.+?)(?:\n|\*\/)/s', $classDoc, $matches)) {
            $classDescription = trim($matches[1]);
            if ($classDescription && $classDescription !== $description) {
                $description .= "\n\n" . $classDescription;
            }
        }

        return $description;
    }

    /**
     * Generate usage example from parameters
     */
    private function generateUsageExample(string $commandName, array $parameters): string
    {
        $usage = $commandName;

        foreach ($parameters as $param) {
            $paramName = $param['name'];
            $isRequired = $param['required'];
            $paramType = $param['type']['name'];

            // Generate parameter format based on type
            $paramFormat = match ($paramType) {
                'bool' => $isRequired ? "--{$paramName}" : "[--{$paramName}]",
                'int', 'float' => $isRequired ? "<{$paramName}>" : "[<{$paramName}>]",
                'string' => $isRequired ? "<{$paramName}>" : "[<{$paramName}>]",
                'array' => $isRequired ? "<{$paramName}...>" : "[<{$paramName}...>]",
                default => $isRequired ? "<{$paramName}>" : "[<{$paramName}>]"
            };

            $usage .= " {$paramFormat}";
        }

        return $usage;
    }

    /**
     * Generate usage examples based on parameter types
     */
    private function generateExamples(string $commandName, array $parameters): array
    {
        $examples = [];

        if (empty($parameters)) {
            return [
                [
                    'command' => $commandName,
                    'description' => 'Execute command with default settings',
                ],
            ];
        }

        // Basic example with required parameters
        $requiredParams = array_filter($parameters, fn ($p) => $p['required']);
        if (! empty($requiredParams)) {
            $basicExample = $commandName;
            $description = 'Execute with required parameters:';

            foreach ($requiredParams as $param) {
                $exampleValue = $this->generateExampleValue($param);
                $basicExample .= " {$exampleValue}";
                $description .= " {$param['name']}";
            }

            $examples[] = [
                'command' => $basicExample,
                'description' => $description,
            ];
        }

        // Advanced example with all parameters
        if (count($parameters) > count($requiredParams)) {
            $advancedExample = $commandName;
            $description = 'Execute with all parameters:';

            foreach ($parameters as $param) {
                $exampleValue = $this->generateExampleValue($param);
                $advancedExample .= " {$exampleValue}";
            }

            $examples[] = [
                'command' => $advancedExample,
                'description' => $description,
            ];
        }

        // Type-specific examples
        $this->addTypeSpecificExamples($examples, $commandName, $parameters);

        return $examples;
    }

    /**
     * Generate example value for a parameter
     */
    private function generateExampleValue(array $param): string
    {
        $name = $param['name'];
        $type = $param['type']['name'];
        $default = $param['default'];

        return match ($type) {
            'bool' => "--{$name}",
            'int' => $this->generateIntExample($name, $default),
            'float' => $this->generateFloatExample($name, $default),
            'string' => $this->generateStringExample($name, $default),
            'array' => $this->generateArrayExample($name),
            default => "\"{$name}_value\""
        };
    }

    /**
     * Generate integer example based on parameter name
     */
    private function generateIntExample(string $name, mixed $default): string
    {
        if ($default !== null) {
            return (string) $default;
        }

        return match (true) {
            str_contains($name, 'port') => '8080',
            str_contains($name, 'count') => '10',
            str_contains($name, 'limit') => '100',
            str_contains($name, 'timeout') => '30',
            str_contains($name, 'id') => '123',
            default => '42'
        };
    }

    /**
     * Generate float example based on parameter name
     */
    private function generateFloatExample(string $name, mixed $default): string
    {
        if ($default !== null) {
            return (string) $default;
        }

        return match (true) {
            str_contains($name, 'rate') => '0.5',
            str_contains($name, 'percent') => '85.5',
            str_contains($name, 'ratio') => '1.25',
            default => '3.14'
        };
    }

    /**
     * Generate string example based on parameter name
     */
    private function generateStringExample(string $name, mixed $default): string
    {
        if ($default !== null && is_string($default)) {
            return "\"{$default}\"";
        }

        return match (true) {
            str_contains($name, 'email') => '"user@example.com"',
            str_contains($name, 'name') => '"example"',
            str_contains($name, 'path') => '"/path/to/file"',
            str_contains($name, 'url') => '"https://example.com"',
            str_contains($name, 'password') => '"secretpassword"',
            str_contains($name, 'key') => '"api_key_123"',
            str_contains($name, 'token') => '"token_abc123"',
            default => "\"{$name}_value\""
        };
    }

    /**
     * Generate array example based on parameter name
     */
    private function generateArrayExample(string $name): string
    {
        return match (true) {
            str_contains($name, 'file') => '"file1.txt,file2.txt"',
            str_contains($name, 'tag') => '"tag1,tag2,tag3"',
            str_contains($name, 'option') => '"option1,option2"',
            default => '"item1,item2,item3"'
        };
    }

    /**
     * Add type-specific usage examples
     */
    private function addTypeSpecificExamples(array &$examples, string $commandName, array $parameters): void
    {
        // Boolean parameter examples
        $boolParams = array_filter($parameters, fn ($p) => $p['type']['name'] === 'bool');
        if (! empty($boolParams)) {
            foreach ($boolParams as $param) {
                $examples[] = [
                    'command' => "{$commandName} --{$param['name']}",
                    'description' => "Enable {$param['name']} option",
                ];
            }
        }

        // File/Path parameter examples
        $pathParams = array_filter(
            $parameters,
            fn ($p) =>
            str_contains($p['name'], 'path') || str_contains($p['name'], 'file')
        );
        if (! empty($pathParams)) {
            $pathParam = reset($pathParams);
            $examples[] = [
                'command' => "{$commandName} \"/absolute/path/to/file\"",
                'description' => "Use absolute path for {$pathParam['name']}",
            ];
        }
    }

    /**
     * Generate help for multiple commands
     */
    public function generateMultipleHelp(array $commands): array
    {
        $helpTexts = [];

        foreach ($commands as $command) {
            $help = $this->generateHelp($command);
            $helpTexts[$help->name] = $help;
        }

        return $helpTexts;
    }

    /**
     * Quick help summary for command overview
     */
    public function generateQuickHelp(object $command, string $methodName = '__invoke'): string
    {
        try {
            $reflection = new ReflectionMethod($command, $methodName);
            $commandInfo = $this->extractCommandInfo($reflection);
            $parameters = $this->parameterInspector->inspectCommand($command, $methodName);

            $summary = $commandInfo['name'];
            if (! empty($commandInfo['description'])) {
                $summary .= " - {$commandInfo['description']}";
            }

            if (! empty($parameters)) {
                $paramCount = count($parameters);
                $requiredCount = count(array_filter($parameters, fn ($p) => $p['required']));
                $summary .= " ({$requiredCount}/{$paramCount} parameters)";
            }

            return $summary;
        } catch (ReflectionException) {
            return get_class($command) . " - No help available";
        }
    }
}
