<?php

namespace Tests\Feature\Settings;

use App\Livewire\Settings\Security;
use App\Models\User;
use App\Support\Confirm\ConfirmToken;
use App\Support\Confirm\InvalidConfirmToken;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;

/**
 * The apply handler is the security gate: it only acts on a CONFIRMED, single-use
 * token. A direct dispatch (bypassing the modal), an unconfirmed token, or a replay
 * must all be a no-op.
 */
class SecurityConfirmTest extends TestCase
{
    use RefreshDatabase;

    private function userWithTotp(): User
    {
        return User::factory()->create([
            'two_factor_secret' => 'S',
            'two_factor_confirmed_at' => now(),
        ]);
    }

    public function test_direct_handler_call_with_a_garbage_token_does_not_disable_2fa(): void
    {
        $user = $this->userWithTotp();

        Livewire::actingAs($user)->test(Security::class)->call('disableTwoFactor', 'forged-token');

        $this->assertTrue($user->fresh()->hasTotp(), 'forged token must not disable 2FA');
    }

    public function test_an_unconfirmed_token_is_rejected_by_the_handler(): void
    {
        // Issuing a token (the opener) is not enough — the modal's confirm() must run
        // first. This closes the "skip the confirmation modal" bypass.
        $user = $this->userWithTotp();
        $this->actingAs($user);
        $token = ConfirmToken::issue('twoFactorDisabled', [], 'two_factor.disable', $user->email);

        Livewire::actingAs($user->fresh())->test(Security::class)->call('disableTwoFactor', $token);

        $this->assertTrue($user->fresh()->hasTotp(), 'a never-confirmed token must not apply');
    }

    public function test_a_confirmed_token_disables_2fa(): void
    {
        $user = $this->userWithTotp();
        $this->actingAs($user);
        $token = ConfirmToken::issue('twoFactorDisabled', [], 'two_factor.disable', $user->email);
        ConfirmToken::confirm($token); // the modal confirmation step

        Livewire::actingAs($user->fresh())->test(Security::class)->call('disableTwoFactor', $token);

        $this->assertFalse($user->fresh()->hasTotp(), 'a confirmed token must disable 2FA');
    }

    public function test_the_handler_burns_the_token_so_it_cannot_be_replayed(): void
    {
        $user = $this->userWithTotp();
        $this->actingAs($user);
        $token = ConfirmToken::issue('twoFactorDisabled', [], 'two_factor.disable', $user->email);
        ConfirmToken::confirm($token);

        Livewire::actingAs($user->fresh())->test(Security::class)->call('disableTwoFactor', $token);
        $this->assertFalse($user->fresh()->hasTotp());

        // Burned: the same token can no longer be confirmed or consumed.
        $this->expectException(InvalidConfirmToken::class);
        ConfirmToken::confirm($token);
    }
}
