<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Persistence\InMemory;

use App\Scolarite\Domain\Exception\GradeNotFoundException;
use App\Scolarite\Domain\Model\Evaluation\EvaluationId;
use App\Scolarite\Domain\Model\Grade\Grade;
use App\Scolarite\Domain\Model\Grade\GradeId;
use App\Scolarite\Domain\Repository\GradeRepository;
use App\Shared\Domain\Tenant\TenantId;

use function array_filter;
use function array_map;
use function array_values;
use function in_array;

use Override;

final class InMemoryGradeRepository implements GradeRepository
{
    /** @var array<string, Grade> */
    private array $byId = [];

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

    #[Override]
    public function saveAppreciation(Grade $grade): void
    {
        $this->byId[(string) $grade->id] = $grade;
    }

    #[Override]
    public function get(GradeId $id, TenantId $tenantId): Grade
    {
        $grade = $this->findById($id, $tenantId);

        if ($grade === null) {
            throw GradeNotFoundException::withId($id);
        }

        return $grade;
    }

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

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

        return $grade;
    }

    #[Override]
    public function findByEvaluation(EvaluationId $evaluationId, TenantId $tenantId): array
    {
        return array_values(array_filter(
            $this->byId,
            static fn (Grade $g): bool => $g->evaluationId->equals($evaluationId)
                && $g->tenantId->equals($tenantId),
        ));
    }

    #[Override]
    public function findByEvaluations(array $evaluationIds, TenantId $tenantId): array
    {
        $idStrings = array_map(static fn (EvaluationId $id): string => (string) $id, $evaluationIds);

        $grouped = [];

        foreach ($this->byId as $grade) {
            $evalIdStr = (string) $grade->evaluationId;

            if ($grade->tenantId->equals($tenantId) && in_array($evalIdStr, $idStrings, true)) {
                $grouped[$evalIdStr][] = $grade;
            }
        }

        return $grouped;
    }

    #[Override]
    public function hasGradesForEvaluation(EvaluationId $evaluationId, TenantId $tenantId): bool
    {
        foreach ($this->byId as $grade) {
            if ($grade->evaluationId->equals($evaluationId) && $grade->tenantId->equals($tenantId)) {
                return true;
            }
        }

        return false;
    }
}
