<?php

namespace Tests\Feature;

use App\Livewire\Settings\Security;
use App\Livewire\Settings\WebauthnKeys;
use App\Models\User;
use App\Models\WebauthnCredential;
use App\Services\WebauthnService;
use App\Support\Confirm\ConfirmToken;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Mockery;
use Tests\TestCase;

class SettingsFactorManagementTest extends TestCase
{
    use RefreshDatabase;

    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }

    public function test_removing_totp_when_it_is_the_only_factor_clears_codes(): void
    {
        $user = User::factory()->create(['two_factor_secret' => 'S', 'two_factor_confirmed_at' => now()]);
        $user->replaceRecoveryCodes();

        $this->actingAs($user);
        $token = ConfirmToken::issue('twoFactorDisabled');
        ConfirmToken::confirm($token);
        Livewire::actingAs($user)->test(Security::class)->call('disableTwoFactor', $token);

        $fresh = $user->fresh();
        $this->assertFalse($fresh->hasTotp());
        $this->assertFalse($fresh->hasRecoveryCodes(), 'last factor removed → codes cleared');
    }

    public function test_removing_totp_keeps_codes_when_a_key_remains(): void
    {
        $user = User::factory()->create(['two_factor_secret' => 'S', 'two_factor_confirmed_at' => now()]);
        WebauthnCredential::create(['user_id' => $user->id, 'name' => 'k', 'credential_id' => 'cid', 'public_key' => '{}', 'sign_count' => 0]);
        $user->replaceRecoveryCodes();

        $this->actingAs($user);
        $token = ConfirmToken::issue('twoFactorDisabled');
        ConfirmToken::confirm($token);
        Livewire::actingAs($user->fresh())->test(Security::class)->call('disableTwoFactor', $token);

        $fresh = $user->fresh();
        $this->assertFalse($fresh->hasTotp());
        $this->assertTrue($fresh->hasWebauthnCredentials());
        $this->assertTrue($fresh->hasRecoveryCodes(), 'key remains → codes kept');
    }

    public function test_a_key_can_be_the_first_factor_without_existing_2fa(): void
    {
        $user = User::factory()->create();

        $svc = Mockery::mock(WebauthnService::class);
        $svc->shouldReceive('available')->andReturnTrue();
        $svc->shouldReceive('registrationOptions')->andReturn(['ok' => true]);
        app()->instance(WebauthnService::class, $svc);

        Livewire::actingAs($user)->test(WebauthnKeys::class)
            ->set('newName', 'YubiKey')
            ->call('options', $svc)
            ->assertReturned(['ok' => true]);
    }

    public function test_removing_the_last_key_clears_codes(): void
    {
        $user = User::factory()->create();
        $cred = WebauthnCredential::create(['user_id' => $user->id, 'name' => 'k', 'credential_id' => 'cid', 'public_key' => '{}', 'sign_count' => 0]);
        $user->replaceRecoveryCodes();

        $svc = Mockery::mock(WebauthnService::class);
        $svc->shouldReceive('available')->andReturnTrue();
        app()->instance(WebauthnService::class, $svc);

        $this->actingAs($user);
        $token = ConfirmToken::issue('webauthnKeyRemoved', ['id' => $cred->id]);
        ConfirmToken::confirm($token);
        Livewire::actingAs($user->fresh())->test(WebauthnKeys::class)->call('remove', $token, $svc);

        $this->assertFalse($user->fresh()->hasRecoveryCodes());
    }
}
