<?php

declare(strict_types=1);

namespace App\Framework\ErrorBoundaries\Middleware;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\ErrorBoundaries\BoundaryConfig;
use App\Framework\ErrorBoundaries\RetryStrategy;

/**
 * Configuration for error boundary middleware
 */
final readonly class MiddlewareConfiguration
{
    public function __construct(
        public bool $enableForAllRoutes = false,
        public array $enabledRoutes = [],
        public array $excludedRoutes = [],
        public bool $enableApiMiddleware = true,
        public bool $enableHealthMiddleware = true,
        public array $routeConfigurations = [],
        public array $apiPaths = ['/api', '/v1', '/v2'],
        public array $healthPaths = [
            '/health/circuit-breakers',
            '/api/health/circuit-breakers',
            '/admin/health/circuit-breakers',
        ],
        public bool $includeDebugInfo = false,
        public int $priority = 100,
    ) {
    }

    /**
     * Create development configuration
     */
    public static function development(): self
    {
        return new self(
            enableForAllRoutes: true,
            enableApiMiddleware: true,
            enableHealthMiddleware: true,
            includeDebugInfo: true,
            excludedRoutes: [
                '/assets/*',
                '/css/*',
                '/js/*',
                '/favicon.ico',
            ],
        );
    }

    /**
     * Create production configuration
     */
    public static function production(): self
    {
        return new self(
            enableForAllRoutes: false,
            enabledRoutes: [
                '/api/*',
                '/admin/*',
                '/critical/*',
            ],
            enableApiMiddleware: true,
            enableHealthMiddleware: true,
            includeDebugInfo: false,
            excludedRoutes: [
                '/assets/*',
                '/public/*',
                '/static/*',
            ],
        );
    }

    /**
     * Create API-only configuration
     */
    public static function apiOnly(): self
    {
        return new self(
            enableForAllRoutes: false,
            enabledRoutes: ['/api/*'],
            enableApiMiddleware: true,
            enableHealthMiddleware: true,
            apiPaths: ['/api', '/v1', '/v2', '/graphql'],
        );
    }

    /**
     * Check if middleware should be enabled for a route
     */
    public function shouldEnableForRoute(string $routePath): bool
    {
        // First check exclusions
        if ($this->isExcludedRoute($routePath)) {
            return false;
        }

        // If enabled for all routes, enable unless excluded
        if ($this->enableForAllRoutes) {
            return true;
        }

        // Check specific enabled routes
        return $this->isEnabledRoute($routePath);
    }

    /**
     * Check if route is specifically excluded
     */
    public function isExcludedRoute(string $routePath): bool
    {
        foreach ($this->excludedRoutes as $pattern) {
            if ($this->matchesPattern($routePath, $pattern)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if route is specifically enabled
     */
    public function isEnabledRoute(string $routePath): bool
    {
        foreach ($this->enabledRoutes as $pattern) {
            if ($this->matchesPattern($routePath, $pattern)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if route is an API path
     */
    public function isApiPath(string $routePath): bool
    {
        foreach ($this->apiPaths as $apiPath) {
            if (str_starts_with($routePath, $apiPath)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if route is a health check path
     */
    public function isHealthPath(string $routePath): bool
    {
        return in_array($routePath, $this->healthPaths, true);
    }

    /**
     * Get configuration for specific route
     */
    public function getRouteConfiguration(string $routePath): ?BoundaryConfig
    {
        // Check for exact match first
        if (isset($this->routeConfigurations[$routePath])) {
            return $this->routeConfigurations[$routePath];
        }

        // Check for pattern matches
        foreach ($this->routeConfigurations as $pattern => $config) {
            if ($this->matchesPattern($routePath, $pattern)) {
                return $config;
            }
        }

        return null;
    }

    /**
     * Add route-specific configuration
     */
    public function withRouteConfiguration(string $routePattern, BoundaryConfig $config): self
    {
        $configurations = $this->routeConfigurations;
        $configurations[$routePattern] = $config;

        return new self(
            enableForAllRoutes: $this->enableForAllRoutes,
            enabledRoutes: $this->enabledRoutes,
            excludedRoutes: $this->excludedRoutes,
            enableApiMiddleware: $this->enableApiMiddleware,
            enableHealthMiddleware: $this->enableHealthMiddleware,
            routeConfigurations: $configurations,
            apiPaths: $this->apiPaths,
            healthPaths: $this->healthPaths,
            includeDebugInfo: $this->includeDebugInfo,
            priority: $this->priority,
        );
    }

    /**
     * Get default route configurations
     */
    public function getDefaultRouteConfigurations(): array
    {
        return [
            '/api/*' => BoundaryConfig::externalService(),
            '/admin/*' => BoundaryConfig::critical(),
            '/auth/*' => BoundaryConfig::failFast(),
            '/public/*' => BoundaryConfig::ui(),
            '/health/*' => new BoundaryConfig(
                maxRetries: 0,
                retryStrategy: RetryStrategy::FIXED,
                baseDelay: Duration::fromMilliseconds(0),
                maxDelay: Duration::fromMilliseconds(0),
                circuitBreakerEnabled: false,
                enableMetrics: true
            ),
        ];
    }

    private function matchesPattern(string $path, string $pattern): bool
    {
        // Convert shell-style wildcards to regex
        $regex = str_replace('*', '.*', preg_quote($pattern, '/'));

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