<?php

declare(strict_types=1);

namespace App\Tests\Unit\Scolarite\Infrastructure\Cache;

use App\Administration\Domain\Model\Subject\SubjectId;
use App\Administration\Domain\Model\User\UserId;
use App\Scolarite\Infrastructure\Cache\CachingStudentAverageRepository;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryStudentAverageRepository;
use App\Shared\Domain\Tenant\TenantId;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

final class CachingStudentAverageRepositoryTest extends TestCase
{
    private const string TENANT_ID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
    private const string STUDENT_ID = '22222222-2222-2222-2222-222222222222';
    private const string SUBJECT_ID = '66666666-6666-6666-6666-666666666666';
    private const string PERIOD_ID = '11111111-1111-1111-1111-111111111111';

    private InMemoryStudentAverageRepository $inner;
    private CachingStudentAverageRepository $cached;

    protected function setUp(): void
    {
        $this->inner = new InMemoryStudentAverageRepository();
        $this->cached = new CachingStudentAverageRepository(
            inner: $this->inner,
            cache: new ArrayAdapter(),
        );
    }

    #[Test]
    public function itCachesSubjectAveragesOnRead(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $studentId = UserId::fromString(self::STUDENT_ID);

        $this->inner->saveSubjectAverage(
            $tenantId,
            $studentId,
            SubjectId::fromString(self::SUBJECT_ID),
            self::PERIOD_ID,
            15.0,
            3,
        );

        $result1 = $this->cached->findSubjectAveragesForStudent($studentId, self::PERIOD_ID, $tenantId);
        $result2 = $this->cached->findSubjectAveragesForStudent($studentId, self::PERIOD_ID, $tenantId);

        self::assertSame([15.0], $result1);
        self::assertSame([15.0], $result2);
    }

    #[Test]
    public function itInvalidatesCacheOnSaveSubjectAverage(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $studentId = UserId::fromString(self::STUDENT_ID);
        $subjectId = SubjectId::fromString(self::SUBJECT_ID);

        $this->cached->saveSubjectAverage($tenantId, $studentId, $subjectId, self::PERIOD_ID, 14.0, 2);

        // Remplir le cache
        $this->cached->findSubjectAveragesForStudent($studentId, self::PERIOD_ID, $tenantId);

        // Mettre à jour → doit invalider le cache
        $this->cached->saveSubjectAverage($tenantId, $studentId, $subjectId, self::PERIOD_ID, 16.0, 3);

        $result = $this->cached->findSubjectAveragesForStudent($studentId, self::PERIOD_ID, $tenantId);

        self::assertSame([16.0], $result);
    }

    #[Test]
    public function itReturnsEmptyArrayWhenNoAverages(): void
    {
        $result = $this->cached->findSubjectAveragesForStudent(
            UserId::fromString(self::STUDENT_ID),
            self::PERIOD_ID,
            TenantId::fromString(self::TENANT_ID),
        );

        self::assertSame([], $result);
    }

    #[Test]
    public function itDelegatesToInnerForGeneralAverage(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $studentId = UserId::fromString(self::STUDENT_ID);

        $this->cached->saveGeneralAverage($tenantId, $studentId, self::PERIOD_ID, 13.5);

        $result = $this->cached->findGeneralAverageForStudent($studentId, self::PERIOD_ID, $tenantId);

        self::assertSame(13.5, $result);
    }

    #[Test]
    public function itInvalidatesCacheOnDeleteSubjectAverage(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $studentId = UserId::fromString(self::STUDENT_ID);
        $subjectId = SubjectId::fromString(self::SUBJECT_ID);

        $this->cached->saveSubjectAverage($tenantId, $studentId, $subjectId, self::PERIOD_ID, 14.0, 2);

        // Remplir le cache
        $this->cached->findSubjectAveragesForStudent($studentId, self::PERIOD_ID, $tenantId);

        // Supprimer → doit invalider le cache
        $this->cached->deleteSubjectAverage($studentId, $subjectId, self::PERIOD_ID, $tenantId);

        $result = $this->cached->findSubjectAveragesForStudent($studentId, self::PERIOD_ID, $tenantId);

        self::assertSame([], $result);
    }

    #[Test]
    public function itCachesMultipleSubjectAverages(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $studentId = UserId::fromString(self::STUDENT_ID);
        $subject2Id = '77777777-7777-7777-7777-777777777777';

        $this->cached->saveSubjectAverage(
            $tenantId,
            $studentId,
            SubjectId::fromString(self::SUBJECT_ID),
            self::PERIOD_ID,
            15.0,
            3,
        );
        $this->cached->saveSubjectAverage(
            $tenantId,
            $studentId,
            SubjectId::fromString($subject2Id),
            self::PERIOD_ID,
            12.0,
            2,
        );

        $result = $this->cached->findSubjectAveragesForStudent($studentId, self::PERIOD_ID, $tenantId);

        self::assertCount(2, $result);
        self::assertContains(15.0, $result);
        self::assertContains(12.0, $result);
    }
}
