<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Infrastructure\Persistence\InMemory;

use App\Administration\Domain\Exception\ActivationTokenNotFoundException;
use App\Administration\Domain\Model\ActivationToken\ActivationToken;
use App\Administration\Domain\Model\ActivationToken\ActivationTokenId;
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryActivationTokenRepository;
use App\Shared\Infrastructure\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class InMemoryActivationTokenRepositoryTest extends TestCase
{
    private InMemoryActivationTokenRepository $repository;

    protected function setUp(): void
    {
        $this->repository = new InMemoryActivationTokenRepository();
    }

    #[Test]
    public function saveAndFindByTokenValue(): void
    {
        $token = $this->createToken();

        $this->repository->save($token);
        $found = $this->repository->findByTokenValue($token->tokenValue);

        self::assertSame($token, $found);
    }

    #[Test]
    public function saveAndGetById(): void
    {
        $token = $this->createToken();

        $this->repository->save($token);
        $found = $this->repository->get($token->id);

        self::assertSame($token, $found);
    }

    #[Test]
    public function findByTokenValueReturnsNullWhenNotFound(): void
    {
        $result = $this->repository->findByTokenValue('non-existent-token');

        self::assertNull($result);
    }

    #[Test]
    public function getThrowsExceptionWhenNotFound(): void
    {
        $this->expectException(ActivationTokenNotFoundException::class);

        $this->repository->get(ActivationTokenId::generate());
    }

    #[Test]
    public function deleteRemovesToken(): void
    {
        $token = $this->createToken();
        $this->repository->save($token);

        $this->repository->delete($token->id);

        self::assertNull($this->repository->findByTokenValue($token->tokenValue));
    }

    #[Test]
    public function deleteRemovesTokenFromIdIndex(): void
    {
        $token = $this->createToken();
        $this->repository->save($token);

        $this->repository->delete($token->id);

        $this->expectException(ActivationTokenNotFoundException::class);
        $this->repository->get($token->id);
    }

    #[Test]
    public function deleteNonExistentTokenDoesNotThrow(): void
    {
        $this->repository->delete(ActivationTokenId::generate());

        $this->addToAssertionCount(1); // No exception thrown
    }

    #[Test]
    public function saveUpdatesExistingToken(): void
    {
        $token = $this->createToken();
        $this->repository->save($token);

        // Modify the token (mark as used)
        $usedAt = new DateTimeImmutable('2026-01-16 10:00:00');
        $token->use($usedAt);
        $this->repository->save($token);

        $found = $this->repository->findByTokenValue($token->tokenValue);

        self::assertTrue($found?->isUsed());
    }

    private function createToken(): ActivationToken
    {
        return ActivationToken::generate(
            userId: '550e8400-e29b-41d4-a716-446655440001',
            email: 'user@example.com',
            tenantId: TenantId::fromString('550e8400-e29b-41d4-a716-446655440002'),
            role: 'ROLE_PARENT',
            schoolName: 'École Alpha',
            createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
        );
    }
}
