<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Persistence\InMemory;

use App\Administration\Domain\Exception\ClasseNotFoundException;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Administration\Domain\Model\SchoolClass\ClassName;
use App\Administration\Domain\Model\SchoolClass\ClassStatus;
use App\Administration\Domain\Model\SchoolClass\SchoolClass;
use App\Administration\Domain\Repository\ClassRepository;
use App\Shared\Domain\Tenant\TenantId;
use Override;

final class InMemoryClassRepository implements ClassRepository
{
    /** @var array<string, SchoolClass> Indexed by ID */
    private array $byId = [];

    /** @var array<string, SchoolClass> Indexed by tenant:year:name */
    private array $byTenantYearName = [];

    #[Override]
    public function save(SchoolClass $class): void
    {
        // If class already exists, remove the old name key (handles renames)
        $existingClass = $this->byId[(string) $class->id] ?? null;
        if ($existingClass !== null) {
            $oldKey = $this->nameKey($existingClass->name, $existingClass->tenantId, $existingClass->academicYearId);
            unset($this->byTenantYearName[$oldKey]);
        }

        $this->byId[(string) $class->id] = $class;
        $this->byTenantYearName[$this->nameKey($class->name, $class->tenantId, $class->academicYearId)] = $class;
    }

    #[Override]
    public function get(ClassId $id): SchoolClass
    {
        $class = $this->findById($id);

        if ($class === null) {
            throw ClasseNotFoundException::withId($id);
        }

        return $class;
    }

    #[Override]
    public function findById(ClassId $id): ?SchoolClass
    {
        return $this->byId[(string) $id] ?? null;
    }

    #[Override]
    public function findByName(
        ClassName $name,
        TenantId $tenantId,
        AcademicYearId $academicYearId,
    ): ?SchoolClass {
        return $this->byTenantYearName[$this->nameKey($name, $tenantId, $academicYearId)] ?? null;
    }

    #[Override]
    public function findActiveByTenantAndYear(
        TenantId $tenantId,
        AcademicYearId $academicYearId,
    ): array {
        $result = [];

        foreach ($this->byId as $class) {
            if ($class->tenantId->equals($tenantId)
                && $class->academicYearId->equals($academicYearId)
                && $class->status === ClassStatus::ACTIVE
            ) {
                $result[] = $class;
            }
        }

        return $result;
    }

    #[Override]
    public function delete(ClassId $id): void
    {
        $class = $this->byId[(string) $id] ?? null;

        if ($class !== null) {
            unset($this->byId[(string) $id]);
            unset($this->byTenantYearName[$this->nameKey($class->name, $class->tenantId, $class->academicYearId)]);
        }
    }

    private function nameKey(ClassName $name, TenantId $tenantId, AcademicYearId $academicYearId): string
    {
        return $tenantId . ':' . $academicYearId . ':' . mb_strtolower((string) $name, 'UTF-8');
    }
}
