<?php

declare(strict_types=1);

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

use App\Shared\Infrastructure\Tenant\TenantConfig;
use App\Shared\Infrastructure\Tenant\TenantId;
use App\Shared\Infrastructure\Tenant\TenantNotFoundException;
use App\Shared\Infrastructure\Tenant\TenantRegistry;
use App\Shared\Infrastructure\Tenant\TenantResolver;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

#[CoversClass(TenantResolver::class)]
final class TenantResolverTest extends TestCase
{
    private TenantRegistry $registry;

    protected function setUp(): void
    {
        $this->registry = $this->createMock(TenantRegistry::class);
    }

    #[Test]
    public function itExtractsSubdomainFromHost(): void
    {
        $resolver = new TenantResolver($this->registry, 'classeo.local');

        $subdomain = $resolver->extractSubdomain('ecole-alpha.classeo.local');

        self::assertSame('ecole-alpha', $subdomain);
    }

    #[Test]
    public function itExtractsSubdomainFromHostWithPort(): void
    {
        $resolver = new TenantResolver($this->registry, 'classeo.local');

        $subdomain = $resolver->extractSubdomain('ecole-alpha.classeo.local:8080');

        self::assertSame('ecole-alpha', $subdomain);
    }

    #[Test]
    public function itReturnsNullForMainDomainWithoutSubdomain(): void
    {
        $resolver = new TenantResolver($this->registry, 'classeo.local');

        $subdomain = $resolver->extractSubdomain('classeo.local');

        self::assertNull($subdomain);
    }

    #[Test]
    public function itReturnsNullForWwwSubdomain(): void
    {
        $resolver = new TenantResolver($this->registry, 'classeo.local');

        $subdomain = $resolver->extractSubdomain('www.classeo.local');

        self::assertNull($subdomain);
    }

    #[Test]
    public function itReturnsNullForApiSubdomain(): void
    {
        $resolver = new TenantResolver($this->registry, 'classeo.local');

        $subdomain = $resolver->extractSubdomain('api.classeo.local');

        self::assertNull($subdomain);
    }

    #[Test]
    public function itResolvesValidTenantFromHost(): 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->registry->method('getBySubdomain')
            ->with('ecole-alpha')
            ->willReturn($config);

        $resolver = new TenantResolver($this->registry, 'classeo.local');

        $resolved = $resolver->resolve('ecole-alpha.classeo.local');

        self::assertTrue($tenantId->equals($resolved->tenantId));
    }

    #[Test]
    public function itThrowsExceptionForNonExistentTenant(): void
    {
        $this->registry->method('getBySubdomain')
            ->with('ecole-inexistant')
            ->willThrowException(TenantNotFoundException::withSubdomain('ecole-inexistant'));

        $resolver = new TenantResolver($this->registry, 'classeo.local');

        $this->expectException(TenantNotFoundException::class);

        $resolver->resolve('ecole-inexistant.classeo.local');
    }

    #[Test]
    public function itThrowsExceptionWhenNoSubdomainInHost(): void
    {
        $resolver = new TenantResolver($this->registry, 'classeo.local');

        $this->expectException(TenantNotFoundException::class);

        $resolver->resolve('classeo.local');
    }

    #[Test]
    #[DataProvider('reservedSubdomainsProvider')]
    public function itRejectsReservedSubdomains(string $subdomain): void
    {
        $resolver = new TenantResolver($this->registry, 'classeo.local');

        $this->expectException(TenantNotFoundException::class);

        $resolver->resolve("{$subdomain}.classeo.local");
    }

    /**
     * @return iterable<string, array{string}>
     */
    public static function reservedSubdomainsProvider(): iterable
    {
        yield 'www' => ['www'];
        yield 'api' => ['api'];
        yield 'admin' => ['admin'];
        yield 'static' => ['static'];
        yield 'cdn' => ['cdn'];
        yield 'mail' => ['mail'];
    }
}
