<?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::CONTROLLER, -10)]
final readonly class ControllerPerformanceMiddleware 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::CONTROLLER)) {
            return $next($context);
        }

        $request = $context->request;

        // Try to get controller info from state manager or request
        $controllerInfo = $this->extractControllerInfo($request, $stateManager);

        $controllerKey = $controllerInfo['controller'] ?? 'unknown_controller';
        $actionKey = $controllerInfo['action'] ?? 'unknown_action';

        $performanceContext = [
            'controller' => $controllerKey,
            'action' => $actionKey,
            'method' => $request->method,
            'path' => $request->path,
        ];

        $timingKey = "controller_{$controllerKey}_{$actionKey}";

        $this->collector->startTiming($timingKey, PerformanceCategory::CONTROLLER, $performanceContext);

        // Count controller invocations
        $this->collector->increment(
            "controller_calls_{$controllerKey}",
            PerformanceCategory::CONTROLLER,
            1,
            $performanceContext
        );

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

            // Record successful controller execution
            $this->collector->increment(
                "controller_success_{$controllerKey}",
                PerformanceCategory::CONTROLLER,
                1,
                $performanceContext
            );

            return $result;
        } catch (\Throwable $e) {
            // Record controller error
            $this->collector->increment(
                "controller_errors_{$controllerKey}",
                PerformanceCategory::CONTROLLER,
                1,
                array_merge($performanceContext, ['error' => $e::class])
            );

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

    private function extractControllerInfo(mixed $request, RequestStateManager $stateManager): array
    {
        // Try to get controller info from various sources

        // Method 1: From state manager
        $controllerData = $stateManager->get('controller_info');
        if ($controllerData) {
            return $controllerData;
        }

        // Method 2: From request attributes/route
        if (isset($request->route)) {
            $handler = $request->route['handler'] ?? null;
            if ($handler && is_string($handler)) {
                return $this->parseControllerString($handler);
            }
        }

        // Method 3: From request path (fallback)
        $path = $request->path ?? '/';
        $segments = explode('/', trim($path, '/'));

        return [
            'controller' => $segments[0] ?? 'home',
            'action' => $segments[1] ?? 'index',
        ];
    }

    private function parseControllerString(string $handler): array
    {
        // Parse strings like "App\Controller\HomeController@index"
        if (str_contains($handler, '@')) {
            [$controller, $action] = explode('@', $handler, 2);
            $controller = basename(str_replace('\\', '/', $controller));

            return ['controller' => $controller, 'action' => $action];
        }

        // Parse class names
        if (str_contains($handler, '\\')) {
            $controller = basename(str_replace('\\', '/', $handler));

            return ['controller' => $controller, 'action' => '__invoke'];
        }

        return ['controller' => $handler, 'action' => 'unknown'];
    }
}
