<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Domain\Model\SchoolBranding;

use App\Administration\Domain\Event\BrandingModifie;
use App\Administration\Domain\Model\SchoolBranding\BrandColor;
use App\Administration\Domain\Model\SchoolBranding\LogoUrl;
use App\Administration\Domain\Model\SchoolBranding\SchoolBranding;
use App\Administration\Domain\Model\SchoolClass\SchoolId;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class SchoolBrandingTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string SCHOOL_ID = '550e8400-e29b-41d4-a716-446655440002';

    #[Test]
    public function creerWithDefaultValues(): void
    {
        $branding = $this->creerBranding();

        self::assertTrue($branding->schoolId->equals(SchoolId::fromString(self::SCHOOL_ID)));
        self::assertTrue($branding->tenantId->equals(TenantId::fromString(self::TENANT_ID)));
        self::assertNull($branding->logoUrl);
        self::assertNull($branding->primaryColor);
        self::assertNull($branding->secondaryColor);
        self::assertNull($branding->accentColor);
    }

    #[Test]
    public function creerRecordsNoEvent(): void
    {
        $branding = $this->creerBranding();

        self::assertCount(0, $branding->pullDomainEvents());
    }

    #[Test]
    public function modifierCouleursUpdatesPrimaryColor(): void
    {
        $branding = $this->creerBranding();
        $primaryColor = new BrandColor('#3B82F6');
        $now = new DateTimeImmutable('2026-02-20 10:00:00');

        $branding->modifierCouleurs(
            primaryColor: $primaryColor,
            secondaryColor: null,
            accentColor: null,
            at: $now,
        );

        self::assertNotNull($branding->primaryColor);
        self::assertTrue($branding->primaryColor->equals($primaryColor));
        self::assertNull($branding->secondaryColor);
        self::assertNull($branding->accentColor);
    }

    #[Test]
    public function modifierCouleursUpdatesAllColors(): void
    {
        $branding = $this->creerBranding();
        $primary = new BrandColor('#3B82F6');
        $secondary = new BrandColor('#1E40AF');
        $accent = new BrandColor('#60A5FA');
        $now = new DateTimeImmutable('2026-02-20 10:00:00');

        $branding->modifierCouleurs(
            primaryColor: $primary,
            secondaryColor: $secondary,
            accentColor: $accent,
            at: $now,
        );

        self::assertNotNull($branding->primaryColor);
        self::assertTrue($branding->primaryColor->equals($primary));
        self::assertNotNull($branding->secondaryColor);
        self::assertTrue($branding->secondaryColor->equals($secondary));
        self::assertNotNull($branding->accentColor);
        self::assertTrue($branding->accentColor->equals($accent));
    }

    #[Test]
    public function modifierCouleursRecordsEvent(): void
    {
        $branding = $this->creerBranding();
        $primaryColor = new BrandColor('#3B82F6');
        $now = new DateTimeImmutable('2026-02-20 10:00:00');

        $branding->modifierCouleurs(
            primaryColor: $primaryColor,
            secondaryColor: null,
            accentColor: null,
            at: $now,
        );

        $events = $branding->pullDomainEvents();
        self::assertCount(1, $events);
        self::assertInstanceOf(BrandingModifie::class, $events[0]);
        self::assertSame($now, $events[0]->occurredOn());
        self::assertTrue($branding->schoolId->value->equals($events[0]->aggregateId()));
    }

    #[Test]
    public function modifierCouleursDoesNotRecordEventWhenNothingChanges(): void
    {
        $branding = $this->creerBranding();
        $primaryColor = new BrandColor('#3B82F6');
        $now = new DateTimeImmutable('2026-02-20 10:00:00');

        $branding->modifierCouleurs(
            primaryColor: $primaryColor,
            secondaryColor: null,
            accentColor: null,
            at: $now,
        );

        $branding->pullDomainEvents();

        $branding->modifierCouleurs(
            primaryColor: $primaryColor,
            secondaryColor: null,
            accentColor: null,
            at: new DateTimeImmutable('2026-02-20 11:00:00'),
        );

        self::assertCount(0, $branding->pullDomainEvents());
    }

    #[Test]
    public function changerLogoSetsLogoUrl(): void
    {
        $branding = $this->creerBranding();
        $logoUrl = new LogoUrl('https://s3.example.com/logos/school.png');
        $now = new DateTimeImmutable('2026-02-20 10:00:00');

        $branding->changerLogo($logoUrl, $now);

        self::assertNotNull($branding->logoUrl);
        self::assertTrue($branding->logoUrl->equals($logoUrl));
    }

    #[Test]
    public function changerLogoRecordsEvent(): void
    {
        $branding = $this->creerBranding();
        $logoUrl = new LogoUrl('https://s3.example.com/logos/school.png');
        $now = new DateTimeImmutable('2026-02-20 10:00:00');

        $branding->changerLogo($logoUrl, $now);

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

    #[Test]
    public function changerLogoDoesNotRecordEventWhenSame(): void
    {
        $branding = $this->creerBranding();
        $logoUrl = new LogoUrl('https://s3.example.com/logos/school.png');
        $now = new DateTimeImmutable('2026-02-20 10:00:00');

        $branding->changerLogo($logoUrl, $now);
        $branding->pullDomainEvents();

        $branding->changerLogo($logoUrl, new DateTimeImmutable('2026-02-20 11:00:00'));

        self::assertCount(0, $branding->pullDomainEvents());
    }

    #[Test]
    public function supprimerLogoRemovesLogo(): void
    {
        $branding = $this->creerBranding();
        $logoUrl = new LogoUrl('https://s3.example.com/logos/school.png');
        $now = new DateTimeImmutable('2026-02-20 10:00:00');

        $branding->changerLogo($logoUrl, $now);
        $branding->pullDomainEvents();

        $branding->supprimerLogo(new DateTimeImmutable('2026-02-20 11:00:00'));

        self::assertNull($branding->logoUrl);
        $events = $branding->pullDomainEvents();
        self::assertCount(1, $events);
        self::assertInstanceOf(BrandingModifie::class, $events[0]);
    }

    #[Test]
    public function supprimerLogoDoesNotRecordEventWhenAlreadyNull(): void
    {
        $branding = $this->creerBranding();

        $branding->supprimerLogo(new DateTimeImmutable('2026-02-20 10:00:00'));

        self::assertCount(0, $branding->pullDomainEvents());
    }

    #[Test]
    public function reinitialiserResetsAllToDefaults(): void
    {
        $branding = $this->creerBranding();
        $now = new DateTimeImmutable('2026-02-20 10:00:00');

        $branding->changerLogo(new LogoUrl('https://s3.example.com/logo.png'), $now);
        $branding->modifierCouleurs(
            primaryColor: new BrandColor('#3B82F6'),
            secondaryColor: new BrandColor('#1E40AF'),
            accentColor: new BrandColor('#60A5FA'),
            at: $now,
        );

        $branding->pullDomainEvents();

        $branding->reinitialiser(new DateTimeImmutable('2026-02-20 12:00:00'));

        self::assertNull($branding->logoUrl);
        self::assertNull($branding->primaryColor);
        self::assertNull($branding->secondaryColor);
        self::assertNull($branding->accentColor);

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

    #[Test]
    public function reinitialiserDoesNotRecordEventWhenAlreadyDefault(): void
    {
        $branding = $this->creerBranding();

        $branding->reinitialiser(new DateTimeImmutable('2026-02-20 10:00:00'));

        self::assertCount(0, $branding->pullDomainEvents());
    }

    #[Test]
    public function estPersonnaliseReturnsFalseByDefault(): void
    {
        $branding = $this->creerBranding();

        self::assertFalse($branding->estPersonnalise());
    }

    #[Test]
    public function estPersonnaliseReturnsTrueWhenColorSet(): void
    {
        $branding = $this->creerBranding();
        $branding->modifierCouleurs(
            primaryColor: new BrandColor('#3B82F6'),
            secondaryColor: null,
            accentColor: null,
            at: new DateTimeImmutable(),
        );

        self::assertTrue($branding->estPersonnalise());
    }

    #[Test]
    public function estPersonnaliseReturnsTrueWhenLogoSet(): void
    {
        $branding = $this->creerBranding();
        $branding->changerLogo(
            new LogoUrl('https://s3.example.com/logo.png'),
            new DateTimeImmutable(),
        );

        self::assertTrue($branding->estPersonnalise());
    }

    #[Test]
    public function reconstituteFromStorage(): void
    {
        $schoolId = SchoolId::fromString(self::SCHOOL_ID);
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $logoUrl = new LogoUrl('https://s3.example.com/logo.png');
        $primary = new BrandColor('#3B82F6');
        $secondary = new BrandColor('#1E40AF');
        $accent = new BrandColor('#60A5FA');
        $createdAt = new DateTimeImmutable('2026-02-01 10:00:00');
        $updatedAt = new DateTimeImmutable('2026-02-15 14:30:00');
        $logoUpdatedAt = new DateTimeImmutable('2026-02-10 09:00:00');

        $branding = SchoolBranding::reconstitute(
            schoolId: $schoolId,
            tenantId: $tenantId,
            logoUrl: $logoUrl,
            logoUpdatedAt: $logoUpdatedAt,
            primaryColor: $primary,
            secondaryColor: $secondary,
            accentColor: $accent,
            createdAt: $createdAt,
            updatedAt: $updatedAt,
        );

        self::assertTrue($branding->schoolId->equals($schoolId));
        self::assertTrue($branding->tenantId->equals($tenantId));
        self::assertNotNull($branding->logoUrl);
        self::assertTrue($branding->logoUrl->equals($logoUrl));
        self::assertNotNull($branding->primaryColor);
        self::assertTrue($branding->primaryColor->equals($primary));
        self::assertNotNull($branding->secondaryColor);
        self::assertTrue($branding->secondaryColor->equals($secondary));
        self::assertNotNull($branding->accentColor);
        self::assertTrue($branding->accentColor->equals($accent));
        self::assertSame($createdAt, $branding->createdAt);
        self::assertSame($updatedAt, $branding->updatedAt);

        self::assertCount(0, $branding->pullDomainEvents());
    }

    private function creerBranding(): SchoolBranding
    {
        return SchoolBranding::creer(
            schoolId: SchoolId::fromString(self::SCHOOL_ID),
            tenantId: TenantId::fromString(self::TENANT_ID),
            createdAt: new DateTimeImmutable('2026-02-01 10:00:00'),
        );
    }
}
