<?php

declare(strict_types=1);

namespace Apexl\Tests\User\FeatureTests;

use apexl\Io\modules\user\entities\RefreshToken;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\Factories\RefreshTokenFactory;
use apexl\Io\modules\user\services\ExpiredRefreshTokenPurger;
use apexl\Io\TestInfrastructure\FeatureTest;
use PHPUnit\Framework\Attributes\Test;
use function apexl\Io\container;

/**
 * `refresh_tokens` holds one row per session, so the table only ever grows —
 * the purge is what keeps dead (hashed) credentials from accumulating.
 */
final class ExpiredRefreshTokenPurgerTest extends FeatureTest
{
    #[Test]
    public function purgesRowsPastTheGraceWindowAndKeepsLiveOnes(): void
    {
        $user = userEntity::factory()->create();
        $factory = container()->get(RefreshTokenFactory::class);

        $live = $factory->create($user->id);
        $justExpired = $this->expire($factory->create($user->id), time() - 60);
        $longExpired = $this->expire($factory->create($user->id), time() - (7 * 86400));

        $purged = container()->get(ExpiredRefreshTokenPurger::class)->purge();

        $this->assertSame(1, $purged);
        $this->assertNotNull(RefreshToken::find($live->id));
        // Inside the default grace window: expired, but not yet swept up.
        $this->assertNotNull(RefreshToken::find($justExpired->id));
        $this->assertNull(RefreshToken::find($longExpired->id));
    }

    #[Test]
    public function purgingAnEmptyTableIsANoOp(): void
    {
        $user = userEntity::factory()->create();
        container()->get(RefreshTokenFactory::class)->create($user->id);

        $this->assertSame(0, container()->get(ExpiredRefreshTokenPurger::class)->purge());
    }

    #[Test]
    public function graceCanBeCollapsedToSweepEverythingExpired(): void
    {
        $user = userEntity::factory()->create();
        $factory = container()->get(RefreshTokenFactory::class);

        $live = $factory->create($user->id);
        $justExpired = $this->expire($factory->create($user->id), time() - 60);

        $this->assertSame(1, container()->get(ExpiredRefreshTokenPurger::class)->purge(0));
        $this->assertNotNull(RefreshToken::find($live->id));
        $this->assertNull(RefreshToken::find($justExpired->id));
    }

    private function expire(RefreshToken $token, int $at): RefreshToken
    {
        $token->expiry = RefreshToken::newExpiryFrom($at, 0);
        $token->store();

        return $token;
    }
}
