<?php

declare(strict_types=1);

namespace App\Framework\Console\Progress;

use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutputInterface;
use App\Framework\Console\ExitCode;
use ReflectionMethod;

/**
 * Middleware that automatically provides progress tracking for commands
 */
final readonly class ProgressMiddleware
{
    public function handle(
        ConsoleInput $input,
        ConsoleOutputInterface $output,
        callable $next,
        ReflectionMethod $method,
        object $instance
    ): ExitCode {
        // Check if command supports progress tracking
        if ($instance instanceof ProgressAwareInterface) {
            return $this->handleProgressAware($input, $output, $next, $instance);
        }

        // Check if method has progress attributes or hints
        if ($this->shouldEnableProgress($method)) {
            return $this->handleWithProgress($input, $output, $next, $method);
        }

        // Default: execute without progress tracking
        return $next($input, $output);
    }

    private function handleProgressAware(
        ConsoleInput $input,
        ConsoleOutputInterface $output,
        callable $next,
        ProgressAwareInterface $instance
    ): ExitCode {
        if (! $instance->supportsProgress()) {
            return $next($input, $output);
        }

        $progressOutput = new ProgressAwareOutput($output);
        $estimatedSteps = $instance->getEstimatedSteps();

        if ($estimatedSteps > 0) {
            // Create progress tracker for determinate operations
            $progress = $progressOutput->createProgress($estimatedSteps, 'Operation');
            $instance->setProgressTracker($progress);
        } else {
            // Create spinner for indeterminate operations
            $spinner = $progressOutput->createSpinner('Working');
            $instance->setProgressTracker($spinner);
        }

        return $next($input, $progressOutput);
    }

    private function handleWithProgress(
        ConsoleInput $input,
        ConsoleOutputInterface $output,
        callable $next,
        ReflectionMethod $method
    ): ExitCode {
        // Wrap output with progress-aware output
        $progressOutput = new ProgressAwareOutput($output);

        // Try to create progress indicator from attribute
        $progress = $this->createProgressFromAttribute($method, $progressOutput);

        if ($progress && $progress instanceof ProgressInterface) {
            // Store progress in output for command to use
            $progressOutput->setCurrentProgress($progress);
        }

        return $next($input, $progressOutput);
    }

    private function shouldEnableProgress(ReflectionMethod $method): bool
    {
        // Check for LongRunning attribute
        $longRunningAttributes = $method->getAttributes(LongRunning::class);
        if (! empty($longRunningAttributes)) {
            return true;
        }

        // Check for other progress-related attributes
        $attributes = $method->getAttributes();
        foreach ($attributes as $attribute) {
            $attributeName = $attribute->getName();
            if (str_contains($attributeName, 'Progress')) {
                return true;
            }
        }

        // Check method name patterns
        $methodName = $method->getName();
        $progressPatterns = [
            'migrate', 'backup', 'restore', 'import', 'export',
            'sync', 'generate', 'compile', 'process', 'analyze',
        ];

        foreach ($progressPatterns as $pattern) {
            if (str_contains(strtolower($methodName), $pattern)) {
                return true;
            }
        }

        return false;
    }

    private function createProgressFromAttribute(
        ReflectionMethod $method,
        ProgressAwareOutput $output
    ): ?ProgressInterface {
        $longRunningAttributes = $method->getAttributes(LongRunning::class);

        if (empty($longRunningAttributes)) {
            return null;
        }

        /** @var LongRunning $longRunning */
        $longRunning = $longRunningAttributes[0]->newInstance();

        return match ($longRunning->progressType) {
            ProgressType::TRACKER => $longRunning->estimatedSteps > 0
                ? $output->createProgress($longRunning->estimatedSteps, $longRunning->title)
                : $output->createProgress(100, $longRunning->title),
            ProgressType::SPINNER => $output->createSpinner($longRunning->title),
            ProgressType::BAR => $output->createProgressBar(),
            ProgressType::NONE => null,
            ProgressType::AUTO => $longRunning->estimatedSteps > 0
                ? $output->createProgress($longRunning->estimatedSteps, $longRunning->title)
                : $output->createSpinner($longRunning->title)
        };
    }
}
