<?php

declare(strict_types=1);

namespace App\Tests\Unit\Shared\Infrastructure\Monitoring;

use App\Shared\Infrastructure\Monitoring\MetricsCollector;
use App\Shared\Infrastructure\Tenant\TenantConfig;
use App\Shared\Infrastructure\Tenant\TenantContext;
use App\Shared\Infrastructure\Tenant\TenantId;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Prometheus\CollectorRegistry;
use Prometheus\Counter;
use Prometheus\Histogram;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\TerminateEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;

/**
 * @see Story 1.8 - T3.4: Custom metrics (requests_total, request_duration_seconds)
 */
#[CoversClass(MetricsCollector::class)]
final class MetricsCollectorTest extends TestCase
{
    private CollectorRegistry $registry;
    private TenantContext $tenantContext;
    private MetricsCollector $collector;
    private Counter $requestsCounter;
    private Histogram $durationHistogram;
    private Counter $loginFailuresCounter;

    protected function setUp(): void
    {
        $this->requestsCounter = $this->createMock(Counter::class);
        $this->durationHistogram = $this->createMock(Histogram::class);
        $this->loginFailuresCounter = $this->createMock(Counter::class);

        $this->registry = $this->createMock(CollectorRegistry::class);
        $this->registry->method('getOrRegisterCounter')
            ->willReturnCallback(fn (string $ns, string $name) => match ($name) {
                'http_requests_total' => $this->requestsCounter,
                'login_failures_total' => $this->loginFailuresCounter,
                default => $this->createMock(Counter::class),
            });
        $this->registry->method('getOrRegisterHistogram')
            ->willReturn($this->durationHistogram);

        $this->tenantContext = new TenantContext();
        $this->collector = new MetricsCollector($this->registry, $this->tenantContext);
    }

    protected function tearDown(): void
    {
        $this->tenantContext->clear();
    }

    #[Test]
    public function itRecordsRequestMetricsWithoutTenant(): void
    {
        $request = Request::create('/api/users', 'GET');
        $request->attributes->set('_route', 'get_users');
        $response = new Response('', 200);

        $kernel = $this->createMock(HttpKernelInterface::class);

        // Simulate request start
        $requestEvent = new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST);
        $this->collector->onKernelRequest($requestEvent);

        // Expect metrics to be recorded with tenant_id="none"
        $this->requestsCounter->expects(self::once())
            ->method('inc')
            ->with(['GET', 'get_users', '200', 'none']);

        $this->durationHistogram->expects(self::once())
            ->method('observe')
            ->with(
                self::greaterThan(0),
                ['GET', 'get_users', 'none'],
            );

        // Simulate request end
        $terminateEvent = new TerminateEvent($kernel, $request, $response);
        $this->collector->onKernelTerminate($terminateEvent);
    }

    #[Test]
    public function itRecordsRequestMetricsWithTenant(): void
    {
        $tenantConfig = new TenantConfig(
            tenantId: TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890'),
            subdomain: 'ecole-alpha',
            databaseUrl: 'postgresql://test@localhost/test',
        );
        $this->tenantContext->setCurrentTenant($tenantConfig);

        $request = Request::create('/api/users', 'POST');
        $request->attributes->set('_route', 'create_user');
        $response = new Response('', 201);

        $kernel = $this->createMock(HttpKernelInterface::class);

        $requestEvent = new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST);
        $this->collector->onKernelRequest($requestEvent);

        $this->requestsCounter->expects(self::once())
            ->method('inc')
            ->with(['POST', 'create_user', '201', 'ecole-alpha']);

        $terminateEvent = new TerminateEvent($kernel, $request, $response);
        $this->collector->onKernelTerminate($terminateEvent);
    }

    #[Test]
    public function itSkipsMetricsEndpoint(): void
    {
        $request = Request::create('/metrics', 'GET');
        $request->attributes->set('_route', 'prometheus_metrics');
        $response = new Response('', 200);

        $kernel = $this->createMock(HttpKernelInterface::class);

        $requestEvent = new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST);
        $this->collector->onKernelRequest($requestEvent);

        // Should NOT record metrics for /metrics endpoint
        $this->requestsCounter->expects(self::never())->method('inc');
        $this->durationHistogram->expects(self::never())->method('observe');

        $terminateEvent = new TerminateEvent($kernel, $request, $response);
        $this->collector->onKernelTerminate($terminateEvent);
    }

    #[Test]
    public function itSkipsHealthEndpoint(): void
    {
        $request = Request::create('/health', 'GET');
        $request->attributes->set('_route', 'health_check');
        $response = new Response('', 200);

        $kernel = $this->createMock(HttpKernelInterface::class);

        $requestEvent = new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST);
        $this->collector->onKernelRequest($requestEvent);

        $this->requestsCounter->expects(self::never())->method('inc');

        $terminateEvent = new TerminateEvent($kernel, $request, $response);
        $this->collector->onKernelTerminate($terminateEvent);
    }

    #[Test]
    public function itRecordsLoginFailureWithoutTenant(): void
    {
        $this->loginFailuresCounter->expects(self::once())
            ->method('inc')
            ->with(['none', 'invalid_credentials']);

        $this->collector->recordLoginFailure();
    }

    #[Test]
    public function itRecordsLoginFailureWithTenant(): void
    {
        $tenantConfig = new TenantConfig(
            tenantId: TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890'),
            subdomain: 'ecole-beta',
            databaseUrl: 'postgresql://test@localhost/test',
        );
        $this->tenantContext->setCurrentTenant($tenantConfig);

        $this->loginFailuresCounter->expects(self::once())
            ->method('inc')
            ->with(['ecole-beta', 'rate_limited']);

        $this->collector->recordLoginFailure('rate_limited');
    }

    #[Test]
    public function itIgnoresSubrequests(): void
    {
        $request = Request::create('/api/test', 'GET');
        $kernel = $this->createMock(HttpKernelInterface::class);

        // Subrequest should be ignored
        $requestEvent = new RequestEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST);
        $this->collector->onKernelRequest($requestEvent);

        $this->requestsCounter->expects(self::never())->method('inc');

        $response = new Response('', 200);
        $terminateEvent = new TerminateEvent($kernel, $request, $response);
        $this->collector->onKernelTerminate($terminateEvent);
    }
}
