<?php

declare(strict_types=1);

namespace App\Framework\ErrorBoundaries\Middleware;

use App\Framework\ErrorBoundaries\BoundaryConfig;
use App\Framework\ErrorBoundaries\ErrorBoundary;
use App\Framework\ErrorBoundaries\ErrorBoundaryFactory;
use App\Framework\Http\HttpMiddleware;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\Next;
use App\Framework\Http\RequestStateManager;
use App\Framework\Http\Responses\JsonResponse;
use App\Framework\Http\Status;
use App\Framework\Logging\Logger;

/**
 * Specialized middleware for API endpoints with detailed error boundaries
 */
final readonly class ApiErrorBoundaryMiddleware implements HttpMiddleware
{
    public function __construct(
        private ErrorBoundaryFactory $boundaryFactory,
        private ?Logger $logger = null,
        private bool $includeDebugInfo = false,
    ) {
    }

    public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
    {
        $request = $context->request;
        $endpoint = $this->extractEndpoint($request);
        $boundary = $this->createApiBoundary($endpoint);

        return $boundary->execute(
            operation: fn () => $next($context, $stateManager),
            fallback: fn () => $context->withResponse($this->createApiErrorResponse($request, $boundary))
        );
    }

    /**
     * Create specialized boundary for API endpoints
     */
    private function createApiBoundary(string $endpoint): ErrorBoundary
    {
        $config = BoundaryConfig::externalService(); // API-friendly config

        return $this->boundaryFactory->create("api_{$endpoint}", $config);
    }

    /**
     * Create comprehensive API error response
     */
    private function createApiErrorResponse($request, ErrorBoundary $boundary): JsonResponse
    {
        $requestId = $request->headers->get('X-Request-ID') ?? uniqid('req_');
        $timestamp = date(\DateTimeInterface::ISO8601);

        $errorData = [
            'error' => [
                'code' => 'API_SERVICE_UNAVAILABLE',
                'message' => 'The API service is temporarily unavailable due to high load or maintenance.',
                'type' => 'service_unavailable',
                'timestamp' => $timestamp,
                'request_id' => $requestId,
                'retry_after' => 60, // seconds
            ],
            'meta' => [
                'endpoint' => $request->uri->getPath(),
                'method' => $request->getMethod()->value,
                'boundary_used' => true,
                'fallback_response' => true,
            ],
            'links' => [
                'status' => '/api/status',
                'documentation' => '/api/docs',
                'support' => '/api/support',
            ],
        ];

        // Add debug information in development
        if ($this->includeDebugInfo) {
            $circuitHealth = $boundary->getCircuitHealth();
            if ($circuitHealth !== null) {
                $errorData['debug'] = [
                    'circuit_breaker' => $circuitHealth,
                    'boundary_name' => $circuitHealth['boundary_name'] ?? 'unknown',
                ];
            }
        }

        $response = new JsonResponse($errorData, Status::SERVICE_UNAVAILABLE);

        // Add retry-after header
        $response = $response->withHeader('Retry-After', '60');
        $response = $response->withHeader('X-Request-ID', $requestId);

        return $response;
    }

    /**
     * Extract API endpoint name for boundary identification
     */
    private function extractEndpoint($request): string
    {
        $path = $request->uri->getPath();
        $method = $request->getMethod()->value;

        // Clean up path for boundary naming
        $cleanPath = trim($path, '/');
        $cleanPath = str_replace(['/', '-', '.'], '_', $cleanPath);
        $cleanPath = preg_replace('/[^a-zA-Z0-9_]/', '', $cleanPath);

        if (empty($cleanPath)) {
            $cleanPath = 'root';
        }

        return strtolower("{$method}_{$cleanPath}");
    }
}
