<?php

declare(strict_types=1);

namespace apexl\Io\modules\user\Factories;

use apexl\Io\modules\user\entities\RefreshToken;
use Exception;
use Random\RandomException;

final readonly class RefreshTokenFactory
{
    /**
     * @throws RandomException
     * @throws Exception
     */
    public function create(int $userId, ?int $lifetime = null): RefreshToken
    {
        $refreshToken = bin2hex(random_bytes(20));

        // One row PER SESSION (device / app), not per user. The old code reused
        // RefreshToken::find($userId) — a single row keyed on user_id — so a
        // login in one place overwrote every other session's token and their
        // refresh calls 404'd ("No record found"), logging them out at the
        // access-token expiry. A fresh row per login lets the PWA, the dashboard
        // and multiple devices each hold their own durable refresh token.
        //
        // $lifetime (seconds) is stored on the row for "Stay signed in" logins so
        // this session lives longer than the config default; NULL = use the
        // default.
        $refreshTokenEntity = new RefreshToken();
        $refreshTokenEntity->user_id = $userId;
        $refreshTokenEntity->token = $refreshToken;
        $refreshTokenEntity->lifetime = $lifetime;
        $refreshTokenEntity->expiry = RefreshToken::newExpiryFrom(null, $lifetime);
        $refreshTokenEntity->store();

        // store() on an INSERT reloads the row and writes the persisted (HASHED)
        // token back onto the entity (HashRefreshTokenBeforeStore hashes only the
        // DB copy) — so without this the caller would hand the client the *hash*,
        // and the next refresh would hash it AGAIN and never match: a silent,
        // session-killing double-hash. Restore the plaintext so the client stores
        // the value RefreshTokenEntityFinder can find. This does not re-persist
        // (no second store()); it only fixes the in-memory value returned via
        // ->token / jsonSerialize().
        $refreshTokenEntity->token = $refreshToken;

        return $refreshTokenEntity;
    }

}
