<?php

declare(strict_types=1);

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

use App\Administration\Domain\Event\ParentDelieDEleve;
use App\Administration\Domain\Event\ParentLieAEleve;
use App\Administration\Domain\Model\StudentGuardian\RelationshipType;
use App\Administration\Domain\Model\StudentGuardian\StudentGuardian;
use App\Administration\Domain\Model\StudentGuardian\StudentGuardianId;
use App\Administration\Domain\Model\User\UserId;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class StudentGuardianTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string STUDENT_ID = '550e8400-e29b-41d4-a716-446655440002';
    private const string GUARDIAN_ID = '550e8400-e29b-41d4-a716-446655440003';
    private const string CREATED_BY_ID = '550e8400-e29b-41d4-a716-446655440004';

    #[Test]
    public function lierCreatesLinkWithAllProperties(): void
    {
        $studentId = UserId::fromString(self::STUDENT_ID);
        $guardianId = UserId::fromString(self::GUARDIAN_ID);
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $createdBy = UserId::fromString(self::CREATED_BY_ID);
        $createdAt = new DateTimeImmutable('2026-02-10 10:00:00');

        $link = StudentGuardian::lier(
            studentId: $studentId,
            guardianId: $guardianId,
            relationshipType: RelationshipType::FATHER,
            tenantId: $tenantId,
            createdAt: $createdAt,
            createdBy: $createdBy,
        );

        self::assertTrue($link->studentId->equals($studentId));
        self::assertTrue($link->guardianId->equals($guardianId));
        self::assertSame(RelationshipType::FATHER, $link->relationshipType);
        self::assertTrue($link->tenantId->equals($tenantId));
        self::assertEquals($createdAt, $link->createdAt);
        self::assertNotNull($link->createdBy);
        self::assertTrue($link->createdBy->equals($createdBy));
    }

    #[Test]
    public function lierRecordsParentLieAEleveEvent(): void
    {
        $link = $this->createLink();

        $events = $link->pullDomainEvents();

        self::assertCount(1, $events);
        self::assertInstanceOf(ParentLieAEleve::class, $events[0]);
        self::assertTrue($events[0]->linkId->equals($link->id));
        self::assertTrue($events[0]->studentId->equals($link->studentId));
        self::assertTrue($events[0]->guardianId->equals($link->guardianId));
        self::assertSame(RelationshipType::FATHER, $events[0]->relationshipType);
        self::assertTrue($events[0]->tenantId->equals($link->tenantId));
    }

    #[Test]
    public function lierWithoutCreatedByAllowsNull(): void
    {
        $link = StudentGuardian::lier(
            studentId: UserId::fromString(self::STUDENT_ID),
            guardianId: UserId::fromString(self::GUARDIAN_ID),
            relationshipType: RelationshipType::MOTHER,
            tenantId: TenantId::fromString(self::TENANT_ID),
            createdAt: new DateTimeImmutable(),
        );

        self::assertNull($link->createdBy);
    }

    #[Test]
    public function lierGeneratesUniqueId(): void
    {
        $link1 = $this->createLink();
        $link2 = $this->createLink();

        self::assertFalse($link1->id->equals($link2->id));
    }

    #[Test]
    public function reconstituteRestoresAllProperties(): void
    {
        $id = StudentGuardianId::generate();
        $studentId = UserId::fromString(self::STUDENT_ID);
        $guardianId = UserId::fromString(self::GUARDIAN_ID);
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $createdBy = UserId::fromString(self::CREATED_BY_ID);
        $createdAt = new DateTimeImmutable('2026-02-10 10:00:00');

        $link = StudentGuardian::reconstitute(
            id: $id,
            studentId: $studentId,
            guardianId: $guardianId,
            relationshipType: RelationshipType::TUTOR_M,
            tenantId: $tenantId,
            createdAt: $createdAt,
            createdBy: $createdBy,
        );

        self::assertTrue($link->id->equals($id));
        self::assertTrue($link->studentId->equals($studentId));
        self::assertTrue($link->guardianId->equals($guardianId));
        self::assertSame(RelationshipType::TUTOR_M, $link->relationshipType);
        self::assertTrue($link->tenantId->equals($tenantId));
        self::assertEquals($createdAt, $link->createdAt);
        self::assertNotNull($link->createdBy);
        self::assertTrue($link->createdBy->equals($createdBy));
        self::assertEmpty($link->pullDomainEvents());
    }

    #[Test]
    public function reconstituteWithNullCreatedBy(): void
    {
        $link = StudentGuardian::reconstitute(
            id: StudentGuardianId::generate(),
            studentId: UserId::fromString(self::STUDENT_ID),
            guardianId: UserId::fromString(self::GUARDIAN_ID),
            relationshipType: RelationshipType::OTHER,
            tenantId: TenantId::fromString(self::TENANT_ID),
            createdAt: new DateTimeImmutable(),
            createdBy: null,
        );

        self::assertNull($link->createdBy);
    }

    #[Test]
    public function delierRecordsParentDelieDEleveEvent(): void
    {
        $link = $this->createLink();
        $link->pullDomainEvents(); // Drain lier() events

        $at = new DateTimeImmutable('2026-02-10 12:00:00');
        $link->delier($at);

        $events = $link->pullDomainEvents();
        self::assertCount(1, $events);
        self::assertInstanceOf(ParentDelieDEleve::class, $events[0]);
        self::assertTrue($events[0]->linkId->equals($link->id));
        self::assertTrue($events[0]->studentId->equals($link->studentId));
        self::assertTrue($events[0]->guardianId->equals($link->guardianId));
        self::assertTrue($events[0]->tenantId->equals($link->tenantId));
        self::assertEquals($at, $events[0]->occurredOn());
    }

    #[Test]
    public function maxGuardiansPerStudentIsTwo(): void
    {
        self::assertSame(2, StudentGuardian::MAX_GUARDIANS_PER_STUDENT);
    }

    private function createLink(): StudentGuardian
    {
        return StudentGuardian::lier(
            studentId: UserId::fromString(self::STUDENT_ID),
            guardianId: UserId::fromString(self::GUARDIAN_ID),
            relationshipType: RelationshipType::FATHER,
            tenantId: TenantId::fromString(self::TENANT_ID),
            createdAt: new DateTimeImmutable('2026-02-10 10:00:00'),
            createdBy: UserId::fromString(self::CREATED_BY_ID),
        );
    }
}
