<?php

declare(strict_types=1);

namespace App\Framework\CommandBus;

use App\Framework\CommandBus\Exceptions\NoHandlerFound;
use App\Framework\CommandBus\Middleware\DatabaseTransactionMiddleware;
use App\Framework\CommandBus\Middleware\PerformanceMonitoringMiddleware;
use App\Framework\Context\ContextType;
use App\Framework\Context\ExecutionContext;
use App\Framework\DI\Container;
use App\Framework\Logging\Logger;
use App\Framework\Queue\Queue;

final class DefaultCommandBus implements CommandBus
{
    public function __construct(
        private CommandHandlersCollection $commandHandlers,
        private Container                 $container,
        private ExecutionContext          $executionContext,
        private Queue                     $queue,
        private Logger                    $logger,
        private array                     $middlewares = [
            PerformanceMonitoringMiddleware::class,
            DatabaseTransactionMiddleware::class,
        ],
    ) {}

    public function dispatch(object $command): mixed
    {
        $contextType = $this->executionContext->getType();
        // Debug logging removed for production - only log errors

        // Context-basierte Queue-Entscheidung
        if ($this->shouldQueueInContext($command, $contextType)) {
            // No logging for normal queue operations in production
            $this->queue->push($command);

            return null;
        }

        // No logging for normal direct execution in production

        return $this->executeCommand($command);
    }

    private function executeCommand(object $command): mixed
    {
        $handlerExecutor = function (object $command): mixed {
            $handler = $this->commandHandlers->get($command::class);

            if ($handler === null) {
                throw NoHandlerFound::forCommand($command::class);
            }

            // $handler = $this->commandHandlers[get_class($command)];
            $handlerClass = $this->container->get($handler->class);

            return $handlerClass->{$handler->method}($command);
        };

        $pipeline = array_reduce(
            array_reverse($this->middlewares),
            function (callable $next, string $middlewareClass): callable {
                return function (object $command) use ($middlewareClass, $next): mixed {
                    /** @var Middleware $middleware */
                    $middleware = $this->container->get($middlewareClass);

                    return $middleware->handle($command, $next);
                };
            },
            $handlerExecutor
        );

        return $pipeline($command);
    }

    private function shouldQueueInContext(object $command, ContextType $context): bool
    {
        $ref = new \ReflectionClass($command);
        $hasQueueAttribute = ! empty($ref->getAttributes(ShouldQueue::class));

        if (! $hasQueueAttribute) {
            return false; // Ohne #[ShouldQueue] Attribut nie queuen
        }

        // Context-basierte Entscheidung
        return match($context) {
            ContextType::WORKER => false,      // Worker: NIEMALS queuen
            ContextType::CONSOLE => false,     // Artisan: meist direkt
            ContextType::TEST => false,        // Tests: immer direkt
            ContextType::CLI_SCRIPT => true,   // CLI Scripts: normal queuen
            ContextType::WEB => true,          // Web: Standard Queue-Verhalten
        };
    }

    public function __debugInfo(): ?array
    {
        return $this->commandHandlers->toArray();
    }
}
