<?php

declare(strict_types=1);

namespace App\Framework\ErrorBoundaries\Async;

use App\Framework\Async\FiberManager;
use App\Framework\DateTime\Clock;
use App\Framework\DateTime\Timer;
use App\Framework\ErrorBoundaries\BoundaryConfig;
use App\Framework\ErrorBoundaries\CircuitBreaker\BoundaryCircuitBreakerManager;
use App\Framework\ErrorBoundaries\Events\BoundaryEventPublisher;
use App\Framework\EventBus\EventBus;
use App\Framework\Logging\Logger;
use App\Framework\StateManagement\StateManagerFactory;

/**
 * Factory for creating async error boundaries
 */
final readonly class AsyncErrorBoundaryFactory
{
    public function __construct(
        private FiberManager $fiberManager,
        private Clock $clock,
        private Timer $timer,
        private ?Logger $logger = null,
        private ?StateManagerFactory $stateManagerFactory = null,
        private ?EventBus $eventBus = null,
    ) {
    }

    /**
     * Create async error boundary
     */
    public function create(string $name, BoundaryConfig $config): AsyncErrorBoundary
    {
        $circuitBreakerManager = null;

        if ($config->circuitBreakerEnabled && $this->stateManagerFactory) {
            $stateManager = $this->stateManagerFactory->createForErrorBoundary();
            $circuitBreakerManager = new BoundaryCircuitBreakerManager($stateManager, $this->logger);
        }

        $eventPublisher = new BoundaryEventPublisher($this->eventBus, $this->logger);

        return new AsyncErrorBoundary(
            boundaryName: $name,
            config: $config,
            fiberManager: $this->fiberManager,
            clock: $this->clock,
            timer: $this->timer,
            logger: $this->logger,
            circuitBreakerManager: $circuitBreakerManager,
            eventPublisher: $eventPublisher,
        );
    }

    /**
     * Create async boundary for external service calls
     */
    public function createForExternalService(string $serviceName): AsyncErrorBoundary
    {
        return $this->create("async_external_{$serviceName}", BoundaryConfig::externalService());
    }

    /**
     * Create async boundary for database operations
     */
    public function createForDatabase(string $operation = 'database'): AsyncErrorBoundary
    {
        return $this->create("async_{$operation}", BoundaryConfig::database());
    }

    /**
     * Create async boundary for background jobs
     */
    public function createForBackgroundJob(string $jobName): AsyncErrorBoundary
    {
        return $this->create("async_job_{$jobName}", BoundaryConfig::backgroundJob());
    }

    /**
     * Create async boundary for critical operations
     */
    public function createForCriticalOperation(string $operationName): AsyncErrorBoundary
    {
        return $this->create("async_critical_{$operationName}", BoundaryConfig::critical());
    }

    /**
     * Create async boundary for batch processing
     */
    public function createForBatchProcessing(string $batchName): AsyncErrorBoundary
    {
        $config = new BoundaryConfig(
            maxRetries: 2,
            retryStrategy: \App\Framework\ErrorBoundaries\RetryStrategy::EXPONENTIAL_JITTER,
            baseDelay: \App\Framework\Core\ValueObjects\Duration::fromMilliseconds(100),
            maxDelay: \App\Framework\Core\ValueObjects\Duration::fromSeconds(5),
            circuitBreakerEnabled: true,
            circuitBreakerThreshold: 10, // Higher threshold for batch operations
            circuitBreakerTimeout: \App\Framework\Core\ValueObjects\Duration::fromMinutes(2),
            enableMetrics: true,
            maxBulkErrorRate: 0.2, // Allow 20% error rate in batch
        );

        return $this->create("async_batch_{$batchName}", $config);
    }

    /**
     * Create async boundary for high-throughput operations
     */
    public function createForHighThroughput(string $operationName): AsyncErrorBoundary
    {
        $config = new BoundaryConfig(
            maxRetries: 1, // Fewer retries for high throughput
            retryStrategy: \App\Framework\ErrorBoundaries\RetryStrategy::FIXED,
            baseDelay: \App\Framework\Core\ValueObjects\Duration::fromMilliseconds(50),
            maxDelay: \App\Framework\Core\ValueObjects\Duration::fromMilliseconds(200),
            circuitBreakerEnabled: true,
            circuitBreakerThreshold: 20, // Higher threshold
            circuitBreakerTimeout: \App\Framework\Core\ValueObjects\Duration::fromSeconds(30),
            enableMetrics: true,
        );

        return $this->create("async_throughput_{$operationName}", $config);
    }
}
