<?php

declare(strict_types=1);

namespace App\Framework\ErrorBoundaries;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\SystemTimer;
use App\Framework\DateTime\Timer;
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 error boundaries with appropriate configurations
 */
final readonly class ErrorBoundaryFactory
{
    private array $routeConfigs;

    public function __construct(
        private ?Timer $timer = null,
        private ?Logger $logger = null,
        private ?StateManagerFactory $stateManagerFactory = null,
        private ?EventBus $eventBus = null,
        array $routeConfigs = []
    ) {
        $this->routeConfigs = array_merge($this->getDefaultRouteConfigs(), $routeConfigs);
    }

    /**
     * Create error boundary for specific route
     */
    public function createForRoute(string $routeName): ErrorBoundary
    {
        $config = $this->getConfigForRoute($routeName);

        return $this->create("route_{$routeName}", $config);
    }

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

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

    /**
     * Create error boundary for UI components
     */
    public function createForUI(string $componentName): ErrorBoundary
    {
        return $this->create("ui_{$componentName}", BoundaryConfig::ui());
    }

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

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

    /**
     * Create custom error boundary
     */
    public function create(string $name, BoundaryConfig $config): ErrorBoundary
    {
        $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 ErrorBoundary(
            boundaryName: $name,
            config: $config,
            timer: $this->timer ?? new SystemTimer(),
            logger: $this->logger,
            circuitBreakerManager: $circuitBreakerManager,
            eventPublisher: $eventPublisher,
        );
    }

    private function getConfigForRoute(string $routeName): BoundaryConfig
    {
        // Check for exact route match
        if (isset($this->routeConfigs[$routeName])) {
            return $this->routeConfigs[$routeName];
        }

        // Check for pattern matches
        foreach ($this->routeConfigs as $pattern => $config) {
            if (str_contains($pattern, '*') && $this->matchesPattern($routeName, $pattern)) {
                return $config;
            }
        }

        // Default configuration
        return $this->routeConfigs['default'];
    }

    private function matchesPattern(string $routeName, string $pattern): bool
    {
        $regex = str_replace('*', '.*', preg_quote($pattern, '/'));

        return (bool) preg_match("/^{$regex}$/", $routeName);
    }

    private function getDefaultRouteConfigs(): array
    {
        return [
            // API routes - more retries and circuit breaker
            'api/*' => BoundaryConfig::externalService(),

            // Admin routes - critical operations
            'admin/*' => BoundaryConfig::critical(),

            // Auth routes - fail fast for security
            'auth/*' => BoundaryConfig::failFast(),

            // Public routes - user-friendly defaults
            'public/*' => BoundaryConfig::ui(),

            // Background job routes
            'job/*' => BoundaryConfig::backgroundJob(),

            // Database operations
            'db/*' => BoundaryConfig::database(),

            // Default fallback
            'default' => new BoundaryConfig(
                maxRetries: 2,
                retryStrategy: RetryStrategy::EXPONENTIAL_JITTER,
                baseDelay: Duration::fromMilliseconds(100),
                maxDelay: Duration::fromSeconds(2),
                circuitBreakerEnabled: true,
                circuitBreakerThreshold: 5,
                circuitBreakerTimeout: Duration::fromMinutes(1),
                enableMetrics: true
            ),

            // HTTP request fallback
            'http_request' => new BoundaryConfig(
                maxRetries: 1,
                retryStrategy: RetryStrategy::FIXED,
                baseDelay: Duration::fromMilliseconds(50),
                maxDelay: Duration::fromMilliseconds(200),
                circuitBreakerEnabled: false,
                enableMetrics: true
            ),
        ];
    }
}
