<?php

declare(strict_types=1);

namespace App\Tests\Unit\SuperAdmin\Domain\Model\Establishment;

use App\Shared\Domain\Tenant\TenantId;
use App\SuperAdmin\Domain\Event\EtablissementCree;
use App\SuperAdmin\Domain\Event\EtablissementDesactive;
use App\SuperAdmin\Domain\Exception\EstablishmentDejaInactifException;
use App\SuperAdmin\Domain\Model\Establishment\Establishment;
use App\SuperAdmin\Domain\Model\Establishment\EstablishmentId;
use App\SuperAdmin\Domain\Model\Establishment\EstablishmentStatus;
use App\SuperAdmin\Domain\Model\SuperAdmin\SuperAdminId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class EstablishmentTest extends TestCase
{
    private const string SUPER_ADMIN_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string ESTABLISHMENT_NAME = 'École Alpha';
    private const string SUBDOMAIN = 'ecole-alpha';

    #[Test]
    public function creerCreatesActiveEstablishment(): void
    {
        $establishment = $this->createEstablishment();

        self::assertSame(EstablishmentStatus::ACTIF, $establishment->status);
        self::assertSame(self::ESTABLISHMENT_NAME, $establishment->name);
        self::assertSame(self::SUBDOMAIN, $establishment->subdomain);
        self::assertNull($establishment->lastActivityAt);
        self::assertNotEmpty($establishment->databaseName);
        self::assertStringStartsWith('classeo_tenant_', $establishment->databaseName);
    }

    #[Test]
    public function creerRecordsEtablissementCreeEvent(): void
    {
        $establishment = $this->createEstablishment();

        $events = $establishment->pullDomainEvents();

        self::assertCount(1, $events);
        self::assertInstanceOf(EtablissementCree::class, $events[0]);
        self::assertTrue($establishment->id->equals($events[0]->establishmentId));
        self::assertTrue($establishment->tenantId->equals($events[0]->tenantId));
        self::assertSame(self::ESTABLISHMENT_NAME, $events[0]->name);
        self::assertSame(self::SUBDOMAIN, $events[0]->subdomain);
    }

    #[Test]
    public function creerGeneratesTenantIdAndDatabaseName(): void
    {
        $establishment = $this->createEstablishment();

        self::assertInstanceOf(TenantId::class, $establishment->tenantId);
        self::assertStringStartsWith('classeo_tenant_', $establishment->databaseName);
    }

    #[Test]
    public function desactiverChangesStatusToInactif(): void
    {
        $establishment = $this->createEstablishment();

        $establishment->desactiver(new DateTimeImmutable('2026-02-16 12:00:00'));

        self::assertSame(EstablishmentStatus::INACTIF, $establishment->status);
    }

    #[Test]
    public function desactiverRecordsEtablissementDesactiveEvent(): void
    {
        $establishment = $this->createEstablishment();
        $establishment->pullDomainEvents(); // Clear creation event

        $establishment->desactiver(new DateTimeImmutable('2026-02-16 12:00:00'));

        $events = $establishment->pullDomainEvents();
        self::assertCount(1, $events);
        self::assertInstanceOf(EtablissementDesactive::class, $events[0]);
    }

    #[Test]
    public function desactiverThrowsWhenAlreadyInactive(): void
    {
        $establishment = $this->createEstablishment();
        $establishment->desactiver(new DateTimeImmutable('2026-02-16 12:00:00'));

        $this->expectException(EstablishmentDejaInactifException::class);
        $establishment->desactiver(new DateTimeImmutable('2026-02-16 13:00:00'));
    }

    #[Test]
    public function enregistrerActiviteUpdatesLastActivityAt(): void
    {
        $establishment = $this->createEstablishment();
        $activityAt = new DateTimeImmutable('2026-02-16 15:00:00');

        $establishment->enregistrerActivite($activityAt);

        self::assertEquals($activityAt, $establishment->lastActivityAt);
    }

    #[Test]
    public function reconstituteRestoresAllProperties(): void
    {
        $id = EstablishmentId::generate();
        $tenantId = TenantId::generate();
        $createdBy = SuperAdminId::fromString(self::SUPER_ADMIN_ID);
        $createdAt = new DateTimeImmutable('2026-01-01 10:00:00');
        $lastActivityAt = new DateTimeImmutable('2026-02-16 14:30:00');

        $establishment = Establishment::reconstitute(
            id: $id,
            tenantId: $tenantId,
            name: self::ESTABLISHMENT_NAME,
            subdomain: self::SUBDOMAIN,
            databaseName: 'classeo_tenant_abc123',
            status: EstablishmentStatus::ACTIF,
            createdAt: $createdAt,
            createdBy: $createdBy,
            lastActivityAt: $lastActivityAt,
        );

        self::assertTrue($id->equals($establishment->id));
        self::assertTrue($tenantId->equals($establishment->tenantId));
        self::assertSame(self::ESTABLISHMENT_NAME, $establishment->name);
        self::assertSame(self::SUBDOMAIN, $establishment->subdomain);
        self::assertSame('classeo_tenant_abc123', $establishment->databaseName);
        self::assertSame(EstablishmentStatus::ACTIF, $establishment->status);
        self::assertEquals($createdAt, $establishment->createdAt);
        self::assertTrue($createdBy->equals($establishment->createdBy));
        self::assertEquals($lastActivityAt, $establishment->lastActivityAt);
        self::assertEmpty($establishment->pullDomainEvents());
    }

    private function createEstablishment(): Establishment
    {
        return Establishment::creer(
            name: self::ESTABLISHMENT_NAME,
            subdomain: self::SUBDOMAIN,
            createdBy: SuperAdminId::fromString(self::SUPER_ADMIN_ID),
            createdAt: new DateTimeImmutable('2026-02-16 10:00:00'),
        );
    }
}
