<?php

declare(strict_types=1);

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

use App\Shared\Infrastructure\Tenant\TenantConfig;
use App\Shared\Infrastructure\Tenant\TenantContext;
use App\Shared\Infrastructure\Tenant\TenantId;
use App\Shared\Infrastructure\Tenant\TenantMiddleware;
use App\Shared\Infrastructure\Tenant\TenantNotFoundException;
use App\Shared\Infrastructure\Tenant\TenantResolver;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;

#[CoversClass(TenantMiddleware::class)]
final class TenantMiddlewareTest extends TestCase
{
    private TenantResolver $resolver;
    private TenantContext $context;

    protected function setUp(): void
    {
        $this->resolver = $this->createMock(TenantResolver::class);
        $this->context = new TenantContext();
    }

    #[Test]
    public function itSetsTenantContextForValidTenant(): void
    {
        $tenantId = TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890');
        $config = new TenantConfig(
            tenantId: $tenantId,
            subdomain: 'ecole-alpha',
            databaseUrl: 'postgresql://user:pass@localhost:5432/classeo_alpha',
        );

        $this->resolver->method('resolve')
            ->with('ecole-alpha.classeo.local')
            ->willReturn($config);

        $middleware = new TenantMiddleware($this->resolver, $this->context);

        $request = Request::create('https://ecole-alpha.classeo.local/api/test');
        $event = $this->createRequestEvent($request);

        $middleware->onKernelRequest($event);

        self::assertTrue($this->context->hasTenant());
        self::assertTrue($tenantId->equals($this->context->getCurrentTenantId()));
    }

    #[Test]
    public function itReturns404ForNonExistentTenant(): void
    {
        $this->resolver->method('resolve')
            ->with('ecole-inexistant.classeo.local')
            ->willThrowException(TenantNotFoundException::withSubdomain('ecole-inexistant'));

        $middleware = new TenantMiddleware($this->resolver, $this->context);

        $request = Request::create('https://ecole-inexistant.classeo.local/api/test');
        $event = $this->createRequestEvent($request);

        $middleware->onKernelRequest($event);

        self::assertTrue($event->hasResponse());
        self::assertSame(Response::HTTP_NOT_FOUND, $event->getResponse()?->getStatusCode());
    }

    #[Test]
    public function itReturnsGenericErrorMessageFor404(): void
    {
        $this->resolver->method('resolve')
            ->willThrowException(TenantNotFoundException::withSubdomain('test'));

        $middleware = new TenantMiddleware($this->resolver, $this->context);

        $request = Request::create('https://test.classeo.local/api/test');
        $event = $this->createRequestEvent($request);

        $middleware->onKernelRequest($event);

        $response = $event->getResponse();
        self::assertNotNull($response);

        $content = json_decode((string) $response->getContent(), true);
        self::assertSame('Resource not found', $content['message']);
        self::assertArrayNotHasKey('subdomain', $content);
    }

    #[Test]
    public function itClearsTenantContextOnTerminate(): void
    {
        $config = new TenantConfig(
            tenantId: TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890'),
            subdomain: 'ecole-alpha',
            databaseUrl: 'postgresql://user:pass@localhost:5432/classeo_alpha',
        );

        $this->context->setCurrentTenant($config);
        self::assertTrue($this->context->hasTenant());

        $middleware = new TenantMiddleware($this->resolver, $this->context);
        $middleware->onKernelTerminate();

        self::assertFalse($this->context->hasTenant());
    }

    #[Test]
    public function itRegistersCorrectEvents(): void
    {
        $events = TenantMiddleware::getSubscribedEvents();

        self::assertArrayHasKey(KernelEvents::REQUEST, $events);
        self::assertArrayHasKey(KernelEvents::TERMINATE, $events);

        // Request listener should have high priority to run early
        $requestConfig = $events[KernelEvents::REQUEST];
        self::assertIsArray($requestConfig);
        self::assertSame('onKernelRequest', $requestConfig[0]);
        self::assertGreaterThan(0, $requestConfig[1]); // High priority
    }

    private function createRequestEvent(Request $request): RequestEvent
    {
        $kernel = $this->createMock(HttpKernelInterface::class);

        return new RequestEvent(
            $kernel,
            $request,
            HttpKernelInterface::MAIN_REQUEST
        );
    }
}
