<?php

declare(strict_types=1);

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

use App\Administration\Domain\Event\CompteActive;
use App\Administration\Domain\Event\CompteCreated;
use App\Administration\Domain\Exception\CompteNonActivableException;
use App\Administration\Domain\Model\ConsentementParental\ConsentementParental;
use App\Administration\Domain\Model\User\Email;
use App\Administration\Domain\Model\User\Role;
use App\Administration\Domain\Model\User\StatutCompte;
use App\Administration\Domain\Model\User\User;
use App\Administration\Domain\Policy\ConsentementParentalPolicy;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class UserTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
    private const string SCHOOL_NAME = 'École Alpha';

    private Clock $clock;
    private ConsentementParentalPolicy $consentementPolicy;

    protected function setUp(): void
    {
        $this->clock = new class implements Clock {
            public function now(): DateTimeImmutable
            {
                return new DateTimeImmutable('2026-01-31 10:00:00');
            }
        };

        $this->consentementPolicy = new ConsentementParentalPolicy($this->clock);
    }

    #[Test]
    public function creerCreatesUserWithPendingStatus(): void
    {
        $user = $this->createUser();

        self::assertSame(StatutCompte::EN_ATTENTE, $user->statut);
        self::assertNull($user->hashedPassword);
        self::assertNull($user->activatedAt);
    }

    #[Test]
    public function creerRecordsCompteCreatedEvent(): void
    {
        $user = $this->createUser();

        $events = $user->pullDomainEvents();

        self::assertCount(1, $events);
        self::assertInstanceOf(CompteCreated::class, $events[0]);
    }

    #[Test]
    public function activerSetsPasswordAndChangesStatusToActive(): void
    {
        $user = $this->createUser();
        $hashedPassword = '$argon2id$hashed';
        $activatedAt = new DateTimeImmutable('2026-01-31 10:00:00');

        $user->activer($hashedPassword, $activatedAt, $this->consentementPolicy);

        self::assertSame(StatutCompte::ACTIF, $user->statut);
        self::assertSame($hashedPassword, $user->hashedPassword);
        self::assertEquals($activatedAt, $user->activatedAt);
    }

    #[Test]
    public function activerRecordsCompteActiveEvent(): void
    {
        $user = $this->createUser();
        $user->pullDomainEvents();

        $user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);

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

    #[Test]
    public function activerThrowsWhenStatusIsNotPending(): void
    {
        $user = $this->createUser();
        $user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);

        $this->expectException(CompteNonActivableException::class);
        $this->expectExceptionMessage('Ce compte est déjà actif.');

        $user->activer('$argon2id$another', new DateTimeImmutable(), $this->consentementPolicy);
    }

    #[Test]
    public function activerThrowsForMinorWithoutConsent(): void
    {
        // Créer un utilisateur mineur (14 ans)
        $user = User::creer(
            email: new Email('eleve@example.com'),
            role: Role::ELEVE,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: self::SCHOOL_NAME,
            dateNaissance: new DateTimeImmutable('2012-06-15'), // 13 ans
            createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
        );

        $this->expectException(CompteNonActivableException::class);
        $this->expectExceptionMessage('consentement parental manquant');

        $user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);
    }

    #[Test]
    public function activerSucceedsForMinorWithConsent(): void
    {
        // Créer un utilisateur mineur (14 ans)
        $user = User::creer(
            email: new Email('eleve@example.com'),
            role: Role::ELEVE,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: self::SCHOOL_NAME,
            dateNaissance: new DateTimeImmutable('2012-06-15'),
            createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
        );

        // Enregistrer le consentement parental
        $consentement = ConsentementParental::accorder(
            parentId: 'parent-uuid',
            eleveId: (string) $user->id,
            at: new DateTimeImmutable('2026-01-20 10:00:00'),
            ipAddress: '192.168.1.1',
        );
        $user->enregistrerConsentementParental($consentement);

        // L'activation devrait maintenant réussir
        $user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);

        self::assertSame(StatutCompte::ACTIF, $user->statut);
    }

    #[Test]
    public function activerSucceedsForAdultWithoutConsent(): void
    {
        // Créer un utilisateur adulte (16 ans)
        $user = User::creer(
            email: new Email('eleve@example.com'),
            role: Role::ELEVE,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: self::SCHOOL_NAME,
            dateNaissance: new DateTimeImmutable('2010-01-01'), // 16 ans
            createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
        );

        // Pas de consentement nécessaire
        $user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);

        self::assertSame(StatutCompte::ACTIF, $user->statut);
    }

    #[Test]
    public function peutSeConnecterReturnsTrueOnlyWhenActive(): void
    {
        $user = $this->createUser();

        self::assertFalse($user->peutSeConnecter());

        $user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);

        self::assertTrue($user->peutSeConnecter());
    }

    #[Test]
    public function necessiteConsentementParentalReturnsTrueForMinor(): void
    {
        $user = User::creer(
            email: new Email('eleve@example.com'),
            role: Role::ELEVE,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: self::SCHOOL_NAME,
            dateNaissance: new DateTimeImmutable('2012-06-15'), // 13 ans
            createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
        );

        self::assertTrue($user->necessiteConsentementParental($this->consentementPolicy));
    }

    #[Test]
    public function necessiteConsentementParentalReturnsFalseForAdult(): void
    {
        $user = User::creer(
            email: new Email('parent@example.com'),
            role: Role::PARENT,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: self::SCHOOL_NAME,
            dateNaissance: null, // Parents n'ont pas de date de naissance enregistrée
            createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
        );

        self::assertFalse($user->necessiteConsentementParental($this->consentementPolicy));
    }

    private function createUser(): User
    {
        return User::creer(
            email: new Email('user@example.com'),
            role: Role::PARENT,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: self::SCHOOL_NAME,
            dateNaissance: null,
            createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
        );
    }
}
