<?php

declare(strict_types=1);

namespace App\Framework\Console\ErrorRecovery;

use App\Framework\Console\CommandList;
use App\Framework\Console\CommandRegistry;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleOutputInterface;
use App\Framework\Exception\Core\ConsoleErrorCode;
use App\Framework\Exception\Core\DatabaseErrorCode;
use App\Framework\Exception\Core\SystemErrorCode;
use App\Framework\Exception\FrameworkException;

final readonly class ErrorRecoveryService
{
    public function __construct(
        private CommandSuggestionEngine $suggestionEngine,
        private CommandList $commandList,
        private CommandRegistry $commandRegistry
    ) {
    }

    public function handleCommandNotFound(string $command, ConsoleOutputInterface $output): void
    {
        $output->writeLine("❌ Command '{$command}' not found.", ConsoleColor::RED);
        $output->newLine();

        $suggestions = $this->suggestionEngine->suggestCommand($command);

        if ($suggestions->hasSuggestions()) {
            $output->writeLine($suggestions->formatForDisplay(), ConsoleColor::YELLOW);

            $this->showAdditionalHelp($command, $output);
        } else {
            $this->showGeneralHelp($output);
        }
    }

    public function handleCommandExecutionError(
        string $command,
        \Throwable $error,
        ConsoleOutputInterface $output
    ): void {
        $output->writeLine("💥 Error executing command '{$command}':", ConsoleColor::RED);
        $output->writeLine("   {$error->getMessage()}", ConsoleColor::RED);
        $output->newLine();

        $recovery = $this->analyzeError($error);

        if ($recovery->hasRecoveryOptions()) {
            $output->writeLine("🔧 Possible solutions:", ConsoleColor::CYAN);

            foreach ($recovery->getRecoveryOptions() as $option) {
                $output->writeLine("  • {$option}", ConsoleColor::WHITE);
            }
            $output->newLine();
        }

        $this->suggestRelatedCommands($command, $output);
    }

    public function handleArgumentError(
        string $command,
        string $argumentError,
        ConsoleOutputInterface $output
    ): void {
        $output->writeLine("⚠️  Argument error in command '{$command}':", ConsoleColor::YELLOW);
        $output->writeLine("   {$argumentError}", ConsoleColor::YELLOW);
        $output->newLine();

        try {
            $help = $this->commandRegistry->generateCommandHelp($command);
            $output->writeLine("💡 Command usage:", ConsoleColor::CYAN);
            $output->writeLine($help, ConsoleColor::WHITE);
        } catch (\Exception $e) {
            $output->writeLine("Unable to generate help for this command.", ConsoleColor::GRAY);
        }
    }

    public function handlePermissionError(
        string $command,
        ConsoleOutputInterface $output
    ): void {
        $output->writeLine("🔒 Permission denied for command '{$command}'.", ConsoleColor::RED);
        $output->newLine();

        $output->writeLine("💡 Try one of these solutions:", ConsoleColor::CYAN);
        $output->writeLine("  • Check if you have the required permissions", ConsoleColor::WHITE);
        $output->writeLine("  • Run the command with appropriate privileges", ConsoleColor::WHITE);
        $output->writeLine("  • Contact your administrator if permissions are required", ConsoleColor::WHITE);
        $output->newLine();

        $this->suggestAlternativeCommands($command, $output);
    }

    public function handleGeneralError(
        string $command,
        \Throwable $error,
        ConsoleOutputInterface $output
    ): void {
        $output->writeLine("❌ Unexpected error in command '{$command}':", ConsoleColor::RED);
        $output->writeLine("   {$error->getMessage()}", ConsoleColor::RED);
        $output->newLine();

        $output->writeLine("🔍 Debugging steps:", ConsoleColor::CYAN);
        $output->writeLine("  • Check the command syntax and arguments", ConsoleColor::WHITE);
        $output->writeLine("  • Verify all required dependencies are available", ConsoleColor::WHITE);
        $output->writeLine("  • Check system resources and permissions", ConsoleColor::WHITE);
        $output->writeLine("  • Try running with verbose output for more details", ConsoleColor::WHITE);
        $output->newLine();

        if ($error instanceof FrameworkException) {
            $this->handleFrameworkError($error, $output);
        }
    }

    private function analyzeError(\Throwable $error): ErrorRecoveryAnalysis
    {
        $options = [];

        if ($error instanceof FrameworkException) {
            $options = match ($error->getErrorCode()) {
                ConsoleErrorCode::COMMAND_NOT_FOUND => [
                    'Check if the command name is spelled correctly',
                    'Use "help" to see all available commands',
                    'Check if the command is properly registered',
                ],
                ConsoleErrorCode::INVALID_ARGUMENT => [
                    'Check the command usage with "help {command}"',
                    'Verify all required arguments are provided',
                    'Check argument types and formats',
                ],
                DatabaseErrorCode::CONNECTION_FAILED => [
                    'Check database connection settings',
                    'Verify database server is running',
                    'Check network connectivity to database',
                ],
                SystemErrorCode::DEPENDENCY_MISSING => [
                    'Check if all required dependencies are installed',
                    'Verify service configuration',
                    'Check for missing environment variables',
                ],
                default => [
                    'Check the error message for specific details',
                    'Verify system configuration',
                    'Try the command with different parameters',
                ]
            };
        } else {
            $options = match (true) {
                str_contains($error->getMessage(), 'Permission denied') => [
                    'Check file and directory permissions',
                    'Run with appropriate user privileges',
                    'Verify write access to required directories',
                ],
                str_contains($error->getMessage(), 'Connection') => [
                    'Check network connectivity',
                    'Verify service endpoints are accessible',
                    'Check firewall and proxy settings',
                ],
                str_contains($error->getMessage(), 'Memory') => [
                    'Increase PHP memory limit',
                    'Optimize command for large datasets',
                    'Process data in smaller batches',
                ],
                default => [
                    'Check the command syntax and arguments',
                    'Verify system configuration',
                    'Try running the command again',
                ]
            };
        }

        return new ErrorRecoveryAnalysis($error, $options);
    }

    private function handleFrameworkError(FrameworkException $error, ConsoleOutputInterface $output): void
    {
        if ($error->hasRetryAfter()) {
            $retryAfter = $error->getRetryAfter();
            $output->writeLine("⏳ This operation can be retried after {$retryAfter} seconds.", ConsoleColor::YELLOW);
            $output->newLine();
        }

        $context = $error->getContext();
        if (! empty($context)) {
            $output->writeLine("🔍 Error context:", ConsoleColor::GRAY);
            foreach ($context as $key => $value) {
                if (is_string($value) || is_numeric($value)) {
                    $output->writeLine("   {$key}: {$value}", ConsoleColor::GRAY);
                }
            }
            $output->newLine();
        }
    }

    private function showAdditionalHelp(string $command, ConsoleOutputInterface $output): void
    {
        $output->newLine();
        $output->writeLine("💡 Additional help:", ConsoleColor::CYAN);
        $output->writeLine("  • Use 'help' to see all available commands", ConsoleColor::WHITE);
        $output->writeLine("  • Use 'help {command}' to get detailed help for a specific command", ConsoleColor::WHITE);

        // Suggest commands from same category
        $namespace = $this->extractNamespace($command);
        if (! empty($namespace)) {
            $categoryCommands = $this->suggestionEngine->suggestCommandsByCategory($namespace);
            if (! empty($categoryCommands)) {
                $output->writeLine(
                    "  • Other '{$namespace}' commands: " .
                    implode(', ', array_slice(array_map(fn ($cmd) => $cmd->name, $categoryCommands), 0, 3)),
                    ConsoleColor::WHITE
                );
            }
        }
    }

    private function showGeneralHelp(ConsoleOutputInterface $output): void
    {
        $output->writeLine("💡 Available help:", ConsoleColor::CYAN);
        $output->writeLine("  • Use 'help' to see all available commands", ConsoleColor::WHITE);
        $output->writeLine("  • Use 'list' to browse commands by category", ConsoleColor::WHITE);
        $output->writeLine("  • Check command spelling and try again", ConsoleColor::WHITE);
    }

    private function suggestRelatedCommands(string $command, ConsoleOutputInterface $output): void
    {
        try {
            $commandObj = $this->commandList->get($command);
            $related = $this->suggestionEngine->suggestSimilarCommands($commandObj, 3);

            if (! empty($related)) {
                $output->writeLine("🔗 Related commands:", ConsoleColor::CYAN);
                foreach ($related as $rel) {
                    $output->writeLine(
                        "  • {$rel['command']->name} - {$rel['relation']->getDisplayText()}",
                        ConsoleColor::WHITE
                    );
                }
                $output->newLine();
            }
        } catch (\Exception $e) {
            // Command not found, skip related suggestions
        }
    }

    private function suggestAlternativeCommands(string $command, ConsoleOutputInterface $output): void
    {
        $namespace = $this->extractNamespace($command);
        if (! empty($namespace)) {
            $alternatives = $this->suggestionEngine->suggestCommandsByCategory($namespace);

            if (! empty($alternatives)) {
                $output->writeLine("🔀 Alternative commands in '{$namespace}' category:", ConsoleColor::CYAN);
                foreach (array_slice($alternatives, 0, 5) as $alt) {
                    $output->writeLine("  • {$alt->name} - {$alt->description}", ConsoleColor::WHITE);
                }
                $output->newLine();
            }
        }
    }

    private function extractNamespace(string $commandName): string
    {
        $parts = explode(':', $commandName);

        return count($parts) > 1 ? $parts[0] : '';
    }
}
