<?php

declare(strict_types=1);

namespace Apexl\Tests\User\FeatureTests;

use apexl\Io\exceptions\RecordNotFoundException;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\exceptions\RefreshTokenExpiredException;
use apexl\Io\modules\user\Factories\RefreshTokenFactory;
use apexl\Io\modules\user\interfaces\TokenHasherInterface;
use apexl\Io\modules\user\services\RefreshTokenEntityFinder;
use apexl\Io\TestInfrastructure\FeatureTest;
use DI\DependencyException;
use DI\NotFoundException;
use PHPUnit\Framework\Attributes\Test;
use function apexl\Io\container;

/**
 * A FeatureTest, not a UnitTest: this exercises store() against a real row, and
 * the hashing it asserts on lives in HashRefreshTokenBeforeStore — a hook
 * registered by userModule. UnitTest only builds a container and never
 * initialises modules, so under it no hook fires, the plaintext token is what
 * lands in the row, and the finder's hashed lookup can never match.
 */
final class RefreshTokenFactoryTest extends FeatureTest
{
    /**
     * The factory MUST return the plaintext token to the caller, not the hash
     * that gets persisted.
     *
     * store() on an INSERT reloads the row and writes the DB copy (hashed by the
     * HashRefreshTokenBeforeStore hook) back onto the entity. Before the fix the
     * caller therefore received the hash; a client that stored it and later
     * refreshed would send the hash, which the finder hashes AGAIN — a
     * double-hash that never matches the stored row, so the refresh 404s and the
     * session dies. This proves the returned value round-trips through the
     * finder.
     *
     * @throws DependencyException
     * @throws NotFoundException
     * @throws RecordNotFoundException
     * @throws RefreshTokenExpiredException
     */
    #[Test]
    public function returnsPlaintextTokenThatTheFinderCanResolve(): void
    {
        $user = userEntity::factory()->create();
        $hasher = container()->get(TokenHasherInterface::class);

        $created = container()->get(RefreshTokenFactory::class)->create($user->id);

        // The finder hashes the value it is given and looks up the row by that
        // hash. Handing it the factory's returned token must locate the row we
        // just created — impossible if create() returned the already-hashed
        // value (it would be hashed a second time and miss).
        $found = container()->make(RefreshTokenEntityFinder::class, [
            'tokenHasher' => $hasher,
        ])->fromToken($created->token);

        $this->assertSame($user->id, $found->user_id);
        $this->assertSame($hasher->hash($created->token), $found->token);
    }
}
