<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Persistence\InMemory;

use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Administration\Domain\Model\Subject\SubjectId;
use App\Administration\Domain\Model\User\UserId;
use App\Scolarite\Domain\Exception\EvaluationNotFoundException;
use App\Scolarite\Domain\Model\Evaluation\Evaluation;
use App\Scolarite\Domain\Model\Evaluation\EvaluationId;
use App\Scolarite\Domain\Model\Evaluation\EvaluationStatus;
use App\Scolarite\Domain\Repository\EvaluationRepository;
use App\Shared\Domain\Tenant\TenantId;

use function array_filter;
use function array_values;

use DateTimeImmutable;
use Override;

final class InMemoryEvaluationRepository implements EvaluationRepository
{
    /** @var array<string, Evaluation> */
    private array $byId = [];

    #[Override]
    public function save(Evaluation $evaluation): void
    {
        $this->byId[(string) $evaluation->id] = $evaluation;
    }

    #[Override]
    public function get(EvaluationId $id, TenantId $tenantId): Evaluation
    {
        $evaluation = $this->findById($id, $tenantId);

        if ($evaluation === null) {
            throw EvaluationNotFoundException::withId($id);
        }

        return $evaluation;
    }

    #[Override]
    public function findById(EvaluationId $id, TenantId $tenantId): ?Evaluation
    {
        $evaluation = $this->byId[(string) $id] ?? null;

        if ($evaluation === null || !$evaluation->tenantId->equals($tenantId)) {
            return null;
        }

        return $evaluation;
    }

    #[Override]
    public function findByTeacher(UserId $teacherId, TenantId $tenantId): array
    {
        return array_values(array_filter(
            $this->byId,
            static fn (Evaluation $e): bool => $e->teacherId->equals($teacherId)
                && $e->tenantId->equals($tenantId)
                && $e->status !== EvaluationStatus::DELETED,
        ));
    }

    #[Override]
    public function findByClass(ClassId $classId, TenantId $tenantId): array
    {
        return array_values(array_filter(
            $this->byId,
            static fn (Evaluation $e): bool => $e->classId->equals($classId)
                && $e->tenantId->equals($tenantId)
                && $e->status !== EvaluationStatus::DELETED,
        ));
    }

    #[Override]
    public function findByTeacherAndClass(UserId $teacherId, ClassId $classId, TenantId $tenantId): array
    {
        return array_values(array_filter(
            $this->byId,
            static fn (Evaluation $e): bool => $e->teacherId->equals($teacherId)
                && $e->classId->equals($classId)
                && $e->tenantId->equals($tenantId)
                && $e->status !== EvaluationStatus::DELETED,
        ));
    }

    #[Override]
    public function findAllWithPublishedGrades(TenantId $tenantId): array
    {
        return array_values(array_filter(
            $this->byId,
            static fn (Evaluation $e): bool => $e->tenantId->equals($tenantId)
                && $e->status !== EvaluationStatus::DELETED
                && $e->notesPubliees(),
        ));
    }

    #[Override]
    public function findWithPublishedGradesBySubjectAndClassInDateRange(
        SubjectId $subjectId,
        ClassId $classId,
        DateTimeImmutable $startDate,
        DateTimeImmutable $endDate,
        TenantId $tenantId,
    ): array {
        return array_values(array_filter(
            $this->byId,
            static fn (Evaluation $e): bool => $e->subjectId->equals($subjectId)
                && $e->classId->equals($classId)
                && $e->tenantId->equals($tenantId)
                && $e->status !== EvaluationStatus::DELETED
                && $e->notesPubliees()
                && $e->evaluationDate >= $startDate
                && $e->evaluationDate <= $endDate,
        ));
    }
}
