<?php

declare(strict_types=1);

namespace Apexl\Tests\User\UnitTests;

use apexl\Config\ConfigurationMock;
use apexl\Io\exceptions\RecordNotFoundException;
use apexl\Io\modules\user\entities\RefreshToken;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\exceptions\RefreshTokenExpiredException;
use apexl\Io\modules\user\interfaces\TokenHasherInterface;
use apexl\Io\modules\user\services\RefreshTokenEntityFinder;
use apexl\Io\TestInfrastructure\UnitTest;
use DI\DependencyException;
use DI\NotFoundException;
use PHPUnit\Framework\Attributes\Test;
use function apexl\Io\container;

final class RefreshTokenEntityFinderTest extends UnitTest
{

    private userEntity $user;
    private string $token;
    /**
     * @var TokenHasherInterface|mixed
     */
    private mixed $tokenHasher;
    private int $defaultExpiry;

    /**
     * @throws RecordNotFoundException
     * @throws DependencyException
     * @throws NotFoundException
     */
    #[Test]
    public function expired(): void
    {
        $this->createRefreshToken(time() - 86400);
        $this->expectException(RefreshTokenExpiredException::class);

        container()->make(RefreshTokenEntityFinder::class, [
            'tokenHasher' => $this->tokenHasher,
        ])->fromToken($this->token);
    }

    private function createRefreshToken(?int $expiry = null): void
    {
        $expiry = $expiry ?? $this->defaultExpiry;

        RefreshToken::factory()->create([
            'user_id' => $this->user->id,
            'token' => $this->tokenHasher->hash($this->token),
            'expiry' => $expiry,
        ]);
    }

    /**
     * @throws NotFoundException
     * @throws DependencyException
     * @throws RefreshTokenExpiredException
     */
    #[Test]
    public function nonExistent(): void
    {
        $this->createRefreshToken();
        $this->expectException(RecordNotFoundException::class);

        container()->make(RefreshTokenEntityFinder::class, [
            'tokenHasher' => $this->tokenHasher,
        ])->fromToken('foo');
    }

    /**
     * @throws RecordNotFoundException
     * @throws DependencyException
     * @throws NotFoundException
     * @throws RefreshTokenExpiredException
     */
    #[Test]
    public function valid(): void
    {
        $this->createRefreshToken();
        $entity = container()->make(RefreshTokenEntityFinder::class, [
            'tokenHasher' => $this->tokenHasher,
        ])->fromToken($this->token);

        $this->assertInstanceOf(RefreshToken::class, $entity);

        $this->assertSame($this->user->id, $entity->user_id);
        $this->assertSame($this->tokenHasher->hash($this->token), $entity->token);
        $this->assertSame($this->defaultExpiry, $entity->expiry->getTimestamp());
    }

    /**
     * @throws DependencyException
     * @throws NotFoundException
     */
    protected function setUp(): void
    {
        parent::setUp();

        $this->defaultExpiry = time() + 86400;

        $this->user = userEntity::factory()->create();

        $this->token = $this->faker->lexify(str_pad('', 255, '?'));

        $config = new ConfigurationMock([
            'auth.refresh.algorithm' => 'sha256',
            'auth.refresh.salt' => $this->faker->lexify(str_pad('', 32, '?')),
        ]);

        $this->tokenHasher = container()->make(TokenHasherInterface::class, [
            'config' => $config,
        ]);
    }
}