<?php

declare(strict_types=1);

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

use App\Shared\Domain\Clock;
use App\Shared\Infrastructure\Tenant\InMemoryTenantRegistry;
use App\Shared\Infrastructure\Tenant\TenantConfig;
use App\Shared\Infrastructure\Tenant\TenantEntityManagerFactory;
use App\Shared\Infrastructure\Tenant\TenantId;
use DateTimeImmutable;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

#[CoversClass(TenantEntityManagerFactory::class)]
final class TenantDatabaseCreationTest extends TestCase
{
    #[Test]
    public function itCreatesConnectionForTenant(): void
    {
        $tenantId = TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890');
        $config = new TenantConfig(
            tenantId: $tenantId,
            subdomain: 'ecole-alpha',
            databaseUrl: 'sqlite:///:memory:',
        );

        $registry = new InMemoryTenantRegistry([$config]);
        $clock = $this->createMock(Clock::class);
        $clock->method('now')->willReturn(new DateTimeImmutable());

        $ormConfig = new Configuration();
        $ormConfig->setProxyDir(sys_get_temp_dir());
        $ormConfig->setProxyNamespace('DoctrineProxies');
        $ormConfig->setAutoGenerateProxyClasses(true);
        $ormConfig->setMetadataDriverImpl(new AttributeDriver([]));
        $ormConfig->enableNativeLazyObjects(true);

        $factory = new TenantEntityManagerFactory($registry, $clock, $ormConfig);

        $em = $factory->getForTenant($tenantId);

        // Verify connection is working
        $connection = $em->getConnection();
        self::assertTrue($connection->isConnected() || $connection->connect());

        // Verify we can execute queries
        $result = $connection->executeQuery('SELECT 1 as test');
        self::assertEquals(1, $result->fetchOne());
    }

    #[Test]
    public function itMaintainsIsolationBetweenTenants(): void
    {
        $tenantIdAlpha = TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890');
        $tenantIdBeta = TenantId::fromString('b2c3d4e5-f6a7-8901-bcde-f12345678901');

        $configAlpha = new TenantConfig(
            tenantId: $tenantIdAlpha,
            subdomain: 'ecole-alpha',
            databaseUrl: 'sqlite:///:memory:',
        );
        $configBeta = new TenantConfig(
            tenantId: $tenantIdBeta,
            subdomain: 'ecole-beta',
            databaseUrl: 'sqlite:///:memory:',
        );

        $registry = new InMemoryTenantRegistry([$configAlpha, $configBeta]);
        $clock = $this->createMock(Clock::class);
        $clock->method('now')->willReturn(new DateTimeImmutable());

        $ormConfig = new Configuration();
        $ormConfig->setProxyDir(sys_get_temp_dir());
        $ormConfig->setProxyNamespace('DoctrineProxies');
        $ormConfig->setAutoGenerateProxyClasses(true);
        $ormConfig->setMetadataDriverImpl(new AttributeDriver([]));
        $ormConfig->enableNativeLazyObjects(true);

        $factory = new TenantEntityManagerFactory($registry, $clock, $ormConfig);

        $emAlpha = $factory->getForTenant($tenantIdAlpha);
        $emBeta = $factory->getForTenant($tenantIdBeta);

        // Create table in Alpha
        $emAlpha->getConnection()->executeStatement(
            'CREATE TABLE test_data (id INTEGER PRIMARY KEY, value TEXT)'
        );
        $emAlpha->getConnection()->executeStatement(
            "INSERT INTO test_data (id, value) VALUES (1, 'alpha_data')"
        );

        // Create different table in Beta
        $emBeta->getConnection()->executeStatement(
            'CREATE TABLE test_data (id INTEGER PRIMARY KEY, value TEXT)'
        );
        $emBeta->getConnection()->executeStatement(
            "INSERT INTO test_data (id, value) VALUES (1, 'beta_data')"
        );

        // Verify isolation - each tenant sees only their data
        $alphaValue = $emAlpha->getConnection()
            ->executeQuery('SELECT value FROM test_data WHERE id = 1')
            ->fetchOne();
        $betaValue = $emBeta->getConnection()
            ->executeQuery('SELECT value FROM test_data WHERE id = 1')
            ->fetchOne();

        self::assertSame('alpha_data', $alphaValue);
        self::assertSame('beta_data', $betaValue);
        self::assertNotSame($alphaValue, $betaValue);
    }
}
