<?php

declare(strict_types=1);

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

use App\SuperAdmin\Domain\Event\SuperAdminCree;
use App\SuperAdmin\Domain\Event\SuperAdminDesactive;
use App\SuperAdmin\Domain\Exception\SuperAdminDejaActifException;
use App\SuperAdmin\Domain\Exception\SuperAdminNonActifException;
use App\SuperAdmin\Domain\Model\SuperAdmin\SuperAdmin;
use App\SuperAdmin\Domain\Model\SuperAdmin\SuperAdminId;
use App\SuperAdmin\Domain\Model\SuperAdmin\SuperAdminStatus;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class SuperAdminTest extends TestCase
{
    private const string EMAIL = 'superadmin@classeo.fr';
    private const string FIRST_NAME = 'Jean';
    private const string LAST_NAME = 'Dupont';
    private const string HASHED_PASSWORD = '$argon2id$hashed';

    #[Test]
    public function creerCreatesActiveSuperAdmin(): void
    {
        $superAdmin = $this->createSuperAdmin();

        self::assertSame(SuperAdminStatus::ACTIF, $superAdmin->status);
        self::assertSame(self::EMAIL, $superAdmin->email);
        self::assertSame(self::FIRST_NAME, $superAdmin->firstName);
        self::assertSame(self::LAST_NAME, $superAdmin->lastName);
        self::assertSame(self::HASHED_PASSWORD, $superAdmin->hashedPassword);
        self::assertNull($superAdmin->lastLoginAt);
    }

    #[Test]
    public function creerRecordsSuperAdminCreeEvent(): void
    {
        $superAdmin = $this->createSuperAdmin();

        $events = $superAdmin->pullDomainEvents();

        self::assertCount(1, $events);
        self::assertInstanceOf(SuperAdminCree::class, $events[0]);
        self::assertSame($superAdmin->id, $events[0]->superAdminId);
        self::assertSame(self::EMAIL, $events[0]->email);
    }

    #[Test]
    public function enregistrerConnexionUpdatesLastLoginAt(): void
    {
        $superAdmin = $this->createSuperAdmin();
        $loginAt = new DateTimeImmutable('2026-02-16 14:30:00');

        $superAdmin->enregistrerConnexion($loginAt);

        self::assertEquals($loginAt, $superAdmin->lastLoginAt);
    }

    #[Test]
    public function enregistrerConnexionThrowsWhenNotActive(): void
    {
        $superAdmin = $this->createSuperAdmin();
        $superAdmin->desactiver(new DateTimeImmutable('2026-02-16 10:00:00'));

        $this->expectException(SuperAdminNonActifException::class);
        $superAdmin->enregistrerConnexion(new DateTimeImmutable('2026-02-16 14:30:00'));
    }

    #[Test]
    public function desactiverChangeStatusToInactif(): void
    {
        $superAdmin = $this->createSuperAdmin();

        $superAdmin->desactiver(new DateTimeImmutable('2026-02-16 10:00:00'));

        self::assertSame(SuperAdminStatus::INACTIF, $superAdmin->status);
    }

    #[Test]
    public function desactiverRecordsSuperAdminDesactiveEvent(): void
    {
        $superAdmin = $this->createSuperAdmin();
        $superAdmin->pullDomainEvents(); // Clear creation event

        $superAdmin->desactiver(new DateTimeImmutable('2026-02-16 10:00:00'));

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

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

        $this->expectException(SuperAdminNonActifException::class);
        $superAdmin->desactiver(new DateTimeImmutable('2026-02-16 11:00:00'));
    }

    #[Test]
    public function reactiverChangesStatusToActif(): void
    {
        $superAdmin = $this->createSuperAdmin();
        $superAdmin->desactiver(new DateTimeImmutable('2026-02-16 10:00:00'));

        $superAdmin->reactiver(new DateTimeImmutable('2026-02-16 11:00:00'));

        self::assertSame(SuperAdminStatus::ACTIF, $superAdmin->status);
    }

    #[Test]
    public function reactiverThrowsWhenAlreadyActive(): void
    {
        $superAdmin = $this->createSuperAdmin();

        $this->expectException(SuperAdminDejaActifException::class);
        $superAdmin->reactiver(new DateTimeImmutable('2026-02-16 11:00:00'));
    }

    #[Test]
    public function peutSeConnecterReturnsTrueWhenActive(): void
    {
        $superAdmin = $this->createSuperAdmin();

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

    #[Test]
    public function peutSeConnecterReturnsFalseWhenInactive(): void
    {
        $superAdmin = $this->createSuperAdmin();
        $superAdmin->desactiver(new DateTimeImmutable('2026-02-16 10:00:00'));

        self::assertFalse($superAdmin->peutSeConnecter());
    }

    #[Test]
    public function reconstituteRestoresAllProperties(): void
    {
        $id = SuperAdminId::generate();
        $createdAt = new DateTimeImmutable('2026-01-01 10:00:00');
        $lastLoginAt = new DateTimeImmutable('2026-02-16 14:30:00');

        $superAdmin = SuperAdmin::reconstitute(
            id: $id,
            email: self::EMAIL,
            hashedPassword: self::HASHED_PASSWORD,
            firstName: self::FIRST_NAME,
            lastName: self::LAST_NAME,
            status: SuperAdminStatus::ACTIF,
            createdAt: $createdAt,
            lastLoginAt: $lastLoginAt,
        );

        self::assertTrue($id->equals($superAdmin->id));
        self::assertSame(self::EMAIL, $superAdmin->email);
        self::assertSame(self::HASHED_PASSWORD, $superAdmin->hashedPassword);
        self::assertSame(self::FIRST_NAME, $superAdmin->firstName);
        self::assertSame(self::LAST_NAME, $superAdmin->lastName);
        self::assertSame(SuperAdminStatus::ACTIF, $superAdmin->status);
        self::assertEquals($createdAt, $superAdmin->createdAt);
        self::assertEquals($lastLoginAt, $superAdmin->lastLoginAt);
        self::assertEmpty($superAdmin->pullDomainEvents());
    }

    private function createSuperAdmin(): SuperAdmin
    {
        return SuperAdmin::creer(
            email: self::EMAIL,
            hashedPassword: self::HASHED_PASSWORD,
            firstName: self::FIRST_NAME,
            lastName: self::LAST_NAME,
            createdAt: new DateTimeImmutable('2026-02-16 10:00:00'),
        );
    }
}
