<?php

declare(strict_types=1);

namespace App\Framework\Performance\Middleware;

use App\Framework\Http\HttpMiddleware;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\MiddlewarePriority;
use App\Framework\Http\MiddlewarePriorityAttribute;
use App\Framework\Http\Next;
use App\Framework\Http\RequestStateManager;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Performance\PerformanceConfig;

#[MiddlewarePriorityAttribute(MiddlewarePriority::ROUTING, 10)]
final readonly class RoutingPerformanceMiddleware implements HttpMiddleware
{
    public function __construct(
        private PerformanceCollectorInterface $collector,
        private PerformanceConfig $config
    ) {
    }

    public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
    {
        if (! $this->config->isTrackingEnabled(PerformanceCategory::ROUTING)) {
            return $next($context);
        }

        $request = $context->request;
        $path = $request->path ?? '/';
        $method = $request->method;

        $routingContext = [
            'method' => $method,
            'path' => $path,
        ];

        $routingKey = 'route_resolution';

        $this->collector->startTiming($routingKey, PerformanceCategory::ROUTING, $routingContext);

        // Count routing attempts
        $this->collector->increment('routing_attempts', PerformanceCategory::ROUTING, 1, $routingContext);

        try {
            $result = $next($context);

            // Check if route was found (this is framework-specific logic)
            $routeFound = $this->isRouteFound($result, $stateManager);

            if ($routeFound) {
                $this->collector->increment('routes_found', PerformanceCategory::ROUTING, 1, $routingContext);

                // Get route pattern if available
                $routePattern = $this->getRoutePattern($result, $stateManager);
                if ($routePattern) {
                    $this->collector->increment(
                        "route_pattern_" . md5($routePattern),
                        PerformanceCategory::ROUTING,
                        1,
                        array_merge($routingContext, ['pattern' => $routePattern])
                    );
                }
            } else {
                $this->collector->increment('routes_not_found', PerformanceCategory::ROUTING, 1, $routingContext);
            }

            return $result;
        } catch (\Throwable $e) {
            $this->collector->increment(
                'routing_errors',
                PerformanceCategory::ROUTING,
                1,
                array_merge($routingContext, ['error' => $e::class])
            );

            throw $e;
        } finally {
            $this->collector->endTiming($routingKey);
        }
    }

    private function isRouteFound(MiddlewareContext $context, RequestStateManager $stateManager): bool
    {
        // Check various indicators that a route was found

        // Method 1: Check state manager
        if ($stateManager->has('route_found')) {
            return $stateManager->get('route_found');
        }

        // Method 2: Check if controller info is available
        if ($stateManager->has('controller_info')) {
            return true;
        }

        // Method 3: Check response status (if not 404)
        if (isset($context->response) && $context->response->status->value !== 404) {
            return true;
        }

        // Default: assume found if we got here without exceptions
        return true;
    }

    private function getRoutePattern(MiddlewareContext $context, RequestStateManager $stateManager): ?string
    {
        // Try to get route pattern from various sources

        // Method 1: From state manager
        $routeData = $stateManager->get('route_data');
        if ($routeData && isset($routeData['pattern'])) {
            return $routeData['pattern'];
        }

        // Method 2: From request route
        if (isset($context->request->route['pattern'])) {
            return $context->request->route['pattern'];
        }

        // Method 3: From matched route
        $matchedRoute = $stateManager->get('matched_route');
        if ($matchedRoute && isset($matchedRoute['pattern'])) {
            return $matchedRoute['pattern'];
        }

        return null;
    }
}
