<?php

declare(strict_types=1);

namespace App\Framework\Webhook\Middleware;

use App\Framework\Core\Environment;
use App\Framework\Http\HttpMiddleware;
use App\Framework\Http\JsonResponse;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\Next;
use App\Framework\Http\RequestStateManager;
use App\Framework\Http\Response;
use App\Framework\Logging\Logger;
use App\Framework\Webhook\Attributes\WebhookEndpoint;
use App\Framework\Webhook\Processing\WebhookRequestHandler;
use App\Framework\Webhook\ValueObjects\WebhookProvider;
use ReflectionMethod;

/**
 * Automatic webhook processing middleware
 * Intercepts routes marked with WebhookEndpoint attribute for automatic handling
 */
final readonly class WebhookMiddleware implements HttpMiddleware
{
    public function __construct(
        private WebhookRequestHandler $webhookHandler,
        private Environment $environment,
        private Logger $logger
    ) {
    }

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

        // Check if route has WebhookEndpoint attribute
        $webhookAttribute = $this->extractWebhookAttribute($stateManager);

        if ($webhookAttribute === null) {
            // No webhook attribute - continue normally
            return $next($context);
        }

        // Get webhook configuration from environment
        $webhookConfig = $this->getWebhookConfig($webhookAttribute->provider);

        if ($webhookConfig === null) {
            $this->logger->warning('Webhook configuration not found', [
                'provider' => $webhookAttribute->provider,
                'endpoint' => $request->getPath(),
            ]);

            // Return error response
            $errorResponse = new JsonResponse([
                'status' => 'error',
                'message' => 'Webhook configuration not found',
                'provider' => $webhookAttribute->provider,
            ], 500);

            return $context->withResponse($errorResponse);
        }

        // Process webhook using WebhookRequestHandler
        try {
            $provider = WebhookProvider::fromString($webhookAttribute->provider);

            $webhookResponse = $this->webhookHandler->handle(
                request: $request,
                provider: $provider,
                secret: $webhookConfig['secret'],
                allowedEvents: $webhookAttribute->events
            );

            // Store webhook processing result in request state
            $stateManager->set('webhook.processed', true);
            $stateManager->set('webhook.provider', $webhookAttribute->provider);
            $stateManager->set('webhook.async', $webhookAttribute->async);

            // Return webhook processing response
            return $context->withResponse($webhookResponse);

        } catch (\Exception $e) {
            $this->logger->error('Webhook middleware processing failed', [
                'provider' => $webhookAttribute->provider,
                'endpoint' => $request->getPath(),
                'error' => $e->getMessage(),
            ]);

            // Store error in request state
            $stateManager->set('webhook.error', $e->getMessage());

            // Return error response
            $errorResponse = new JsonResponse([
                'status' => 'error',
                'message' => 'Webhook processing failed',
                'provider' => $webhookAttribute->provider,
                'error' => $e->getMessage(),
            ], 500);

            return $context->withResponse($errorResponse);
        }
    }

    /**
     * Extract WebhookEndpoint attribute from request state or route info
     */
    private function extractWebhookAttribute(RequestStateManager $stateManager): ?WebhookEndpoint
    {
        // Try to get route info from request state
        $routeInfo = $stateManager->get('_route_info');

        if (! $routeInfo || ! isset($routeInfo['controller'], $routeInfo['method'])) {
            return null;
        }

        try {
            $reflection = new ReflectionMethod($routeInfo['controller'], $routeInfo['method']);
            $attributes = $reflection->getAttributes(WebhookEndpoint::class);

            if (empty($attributes)) {
                return null;
            }

            return $attributes[0]->newInstance();

        } catch (\ReflectionException $e) {
            $this->logger->debug('Could not extract webhook attribute', [
                'controller' => $routeInfo['controller'],
                'method' => $routeInfo['method'],
                'error' => $e->getMessage(),
            ]);

            return null;
        }
    }

    /**
     * Get webhook configuration from environment for provider
     */
    private function getWebhookConfig(string $provider): ?array
    {
        $envKey = strtoupper("WEBHOOK_{$provider}_SECRET");
        $secret = $this->environment->get($envKey);

        if (empty($secret)) {
            return null;
        }

        return [
            'secret' => $secret,
            'provider' => $provider,
        ];
    }
}
