<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Persistence\InMemory;

use App\Administration\Domain\Model\Subject\SubjectId;
use App\Administration\Domain\Model\User\UserId;
use App\Scolarite\Domain\Repository\StudentAverageRepository;
use App\Shared\Domain\Tenant\TenantId;

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

use Override;

final class InMemoryStudentAverageRepository implements StudentAverageRepository
{
    /** @var list<array{tenantId: string, studentId: string, subjectId: string, periodId: string, average: float, gradeCount: int}> */
    private array $subjectAverages = [];

    /** @var list<array{tenantId: string, studentId: string, periodId: string, average: float}> */
    private array $generalAverages = [];

    #[Override]
    public function saveSubjectAverage(
        TenantId $tenantId,
        UserId $studentId,
        SubjectId $subjectId,
        string $periodId,
        float $average,
        int $gradeCount,
    ): void {
        $tenantStr = (string) $tenantId;
        $studentStr = (string) $studentId;
        $subjectStr = (string) $subjectId;

        foreach ($this->subjectAverages as $i => $entry) {
            if ($entry['tenantId'] === $tenantStr
                && $entry['studentId'] === $studentStr
                && $entry['subjectId'] === $subjectStr
                && $entry['periodId'] === $periodId
            ) {
                $this->subjectAverages[$i] = [
                    'tenantId' => $tenantStr,
                    'studentId' => $studentStr,
                    'subjectId' => $subjectStr,
                    'periodId' => $periodId,
                    'average' => $average,
                    'gradeCount' => $gradeCount,
                ];

                return;
            }
        }

        $this->subjectAverages[] = [
            'tenantId' => $tenantStr,
            'studentId' => $studentStr,
            'subjectId' => $subjectStr,
            'periodId' => $periodId,
            'average' => $average,
            'gradeCount' => $gradeCount,
        ];
    }

    #[Override]
    public function saveGeneralAverage(
        TenantId $tenantId,
        UserId $studentId,
        string $periodId,
        float $average,
    ): void {
        $tenantStr = (string) $tenantId;
        $studentStr = (string) $studentId;

        foreach ($this->generalAverages as $i => $entry) {
            if ($entry['tenantId'] === $tenantStr
                && $entry['studentId'] === $studentStr
                && $entry['periodId'] === $periodId
            ) {
                $this->generalAverages[$i] = [
                    'tenantId' => $tenantStr,
                    'studentId' => $studentStr,
                    'periodId' => $periodId,
                    'average' => $average,
                ];

                return;
            }
        }

        $this->generalAverages[] = [
            'tenantId' => $tenantStr,
            'studentId' => $studentStr,
            'periodId' => $periodId,
            'average' => $average,
        ];
    }

    #[Override]
    public function findSubjectAveragesForStudent(
        UserId $studentId,
        string $periodId,
        TenantId $tenantId,
    ): array {
        $studentStr = (string) $studentId;
        $tenantStr = (string) $tenantId;

        $matching = array_filter(
            $this->subjectAverages,
            static fn (array $e): bool => $e['studentId'] === $studentStr
                && $e['periodId'] === $periodId
                && $e['tenantId'] === $tenantStr,
        );

        return array_values(array_map(
            static fn (array $e): float => $e['average'],
            $matching,
        ));
    }

    /** @return array{average: float, gradeCount: int}|null */
    public function findSubjectAverage(
        UserId $studentId,
        SubjectId $subjectId,
        string $periodId,
        TenantId $tenantId,
    ): ?array {
        $studentStr = (string) $studentId;
        $subjectStr = (string) $subjectId;
        $tenantStr = (string) $tenantId;

        foreach ($this->subjectAverages as $entry) {
            if ($entry['studentId'] === $studentStr
                && $entry['subjectId'] === $subjectStr
                && $entry['periodId'] === $periodId
                && $entry['tenantId'] === $tenantStr
            ) {
                return ['average' => $entry['average'], 'gradeCount' => $entry['gradeCount']];
            }
        }

        return null;
    }

    #[Override]
    public function findDetailedSubjectAveragesForStudent(
        UserId $studentId,
        string $periodId,
        TenantId $tenantId,
    ): array {
        $studentStr = (string) $studentId;
        $tenantStr = (string) $tenantId;

        $matching = array_filter(
            $this->subjectAverages,
            static fn (array $e): bool => $e['studentId'] === $studentStr
                && $e['periodId'] === $periodId
                && $e['tenantId'] === $tenantStr,
        );

        return array_values(array_map(
            static fn (array $e): array => [
                'subjectId' => $e['subjectId'],
                'subjectName' => null,
                'average' => $e['average'],
                'gradeCount' => $e['gradeCount'],
            ],
            $matching,
        ));
    }

    #[Override]
    public function findGeneralAverageForStudent(
        UserId $studentId,
        string $periodId,
        TenantId $tenantId,
    ): ?float {
        $studentStr = (string) $studentId;
        $tenantStr = (string) $tenantId;

        foreach ($this->generalAverages as $entry) {
            if ($entry['studentId'] === $studentStr
                && $entry['periodId'] === $periodId
                && $entry['tenantId'] === $tenantStr
            ) {
                return $entry['average'];
            }
        }

        return null;
    }

    #[Override]
    public function deleteSubjectAverage(
        UserId $studentId,
        SubjectId $subjectId,
        string $periodId,
        TenantId $tenantId,
    ): void {
        $studentStr = (string) $studentId;
        $subjectStr = (string) $subjectId;
        $tenantStr = (string) $tenantId;

        $this->subjectAverages = array_values(array_filter(
            $this->subjectAverages,
            static fn (array $e): bool => !($e['studentId'] === $studentStr
                && $e['subjectId'] === $subjectStr
                && $e['periodId'] === $periodId
                && $e['tenantId'] === $tenantStr),
        ));
    }

    #[Override]
    public function deleteGeneralAverage(
        UserId $studentId,
        string $periodId,
        TenantId $tenantId,
    ): void {
        $studentStr = (string) $studentId;
        $tenantStr = (string) $tenantId;

        $this->generalAverages = array_values(array_filter(
            $this->generalAverages,
            static fn (array $e): bool => !($e['studentId'] === $studentStr
                && $e['periodId'] === $periodId
                && $e['tenantId'] === $tenantStr),
        ));
    }
}
