<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Persistence\InMemory;

use App\Administration\Domain\Exception\UserNotFoundException;
use App\Administration\Domain\Model\User\Email;
use App\Administration\Domain\Model\User\Role;
use App\Administration\Domain\Model\User\User;
use App\Administration\Domain\Model\User\UserId;
use App\Administration\Domain\Repository\UserRepository;
use App\Shared\Domain\Tenant\TenantId;
use Override;

final class InMemoryUserRepository implements UserRepository
{
    /** @var array<string, User> Indexed by ID */
    private array $byId = [];

    /** @var array<string, User> Indexed by tenant:email (lowercase) */
    private array $byTenantEmail = [];

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

        if ($user->email !== null) {
            $this->byTenantEmail[$this->emailKey($user->email, $user->tenantId)] = $user;
        }
    }

    #[Override]
    public function get(UserId $id): User
    {
        $user = $this->findById($id);

        if ($user === null) {
            throw UserNotFoundException::withId($id);
        }

        return $user;
    }

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

    #[Override]
    public function findByEmail(Email $email, TenantId $tenantId): ?User
    {
        return $this->byTenantEmail[$this->emailKey($email, $tenantId)] ?? null;
    }

    #[Override]
    public function findAllByTenant(TenantId $tenantId): array
    {
        return array_values(array_filter(
            $this->byId,
            static fn (User $user): bool => $user->tenantId->equals($tenantId),
        ));
    }

    #[Override]
    public function findStudentsByTenant(TenantId $tenantId): array
    {
        return array_values(array_filter(
            $this->byId,
            static fn (User $user): bool => $user->tenantId->equals($tenantId) && $user->aLeRole(Role::ELEVE),
        ));
    }

    private function emailKey(Email $email, TenantId $tenantId): string
    {
        return $tenantId . ':' . strtolower((string) $email);
    }
}
