<?php

declare(strict_types=1);

namespace Apexl\Tests\User\FeatureTests;

use apexl\Io\modules\user\Actions\TwoFactorGateAction;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\enums\TwoFactorGateDecision;
use apexl\Io\TestInfrastructure\FeatureTest;
use Apexl\Tests\User\Helpers\EnrolsTwoFactor;
use Apexl\Tests\User\Helpers\ManagesTwoFactorConfig;
use PHPUnit\Framework\Attributes\Test;
use function apexl\Io\container;

/**
 * The mode x enrolment matrix (Plan 007 §2) plus the enforced-mode grace period.
 */
final class TwoFactorGateActionTest extends FeatureTest
{
    use ManagesTwoFactorConfig;
    use EnrolsTwoFactor;

    protected function tearDown(): void
    {
        $this->restoreTwoFactorConfig();
        parent::tearDown();
    }

    private function gate(): TwoFactorGateAction
    {
        return container()->get(TwoFactorGateAction::class);
    }

    /** Enrol while mode is optional, then switch to the mode under test. */
    private function enrolledUserUnderMode(string $mode): userEntity
    {
        $this->setTwoFactorMode('optional');
        $user = $this->makeUser();
        $this->enrolUser($user);
        $this->setTwoFactorMode($mode);

        return $user;
    }

    #[Test]
    public function offModeNeverGates(): void
    {
        $enrolled = $this->enrolledUserUnderMode('off');
        $unenrolled = $this->makeUser();

        $this->assertSame(TwoFactorGateDecision::NONE, $this->gate()->evaluate($enrolled));
        $this->assertSame(TwoFactorGateDecision::NONE, $this->gate()->evaluate($unenrolled));
    }

    #[Test]
    public function optionalGatesOnlyEnrolledUsers(): void
    {
        $enrolled = $this->enrolledUserUnderMode('optional');
        $unenrolled = $this->makeUser();

        $this->assertSame(TwoFactorGateDecision::CHALLENGE, $this->gate()->evaluate($enrolled));
        $this->assertSame(TwoFactorGateDecision::NONE, $this->gate()->evaluate($unenrolled));
    }

    #[Test]
    public function enforcedChallengesEnrolledAndRequiresSetupOtherwise(): void
    {
        $enrolled = $this->enrolledUserUnderMode('enforced');
        $this->setTwoFactorConfig(['auth.two_factor.grace_period_days' => 0]);
        $unenrolled = $this->makeUser();

        $this->assertSame(TwoFactorGateDecision::CHALLENGE, $this->gate()->evaluate($enrolled));
        $this->assertSame(TwoFactorGateDecision::SETUP_REQUIRED, $this->gate()->evaluate($unenrolled));
    }

    #[Test]
    public function enforcedGracePeriodDefersSetupForRecentAccounts(): void
    {
        $this->setTwoFactorConfig([
            'auth.two_factor.mode' => 'enforced',
            'auth.two_factor.grace_period_days' => 7,
        ]);

        // Freshly created (within grace) — nagged but not blocked.
        $recent = $this->makeUser(['created' => time()]);
        $this->assertSame(TwoFactorGateDecision::NONE, $this->gate()->evaluate($recent));

        // Created 10 days ago — grace expired, must set up.
        $old = $this->makeUser(['created' => time() - (10 * 86400)]);
        $this->assertSame(TwoFactorGateDecision::SETUP_REQUIRED, $this->gate()->evaluate($old));
    }
}
