<?php

use App\Livewire\Admin\Settings;
use App\Livewire\Admin\TwoFactorSetup;
use App\Models\Operator;
use App\Support\Settings as AppSettings;
use Livewire\Livewire;
use PragmaRX\Google2FA\Google2FA;

it('is voluntary by default', function () {
    expect(AppSettings::bool('console.require_2fa', false))->toBeFalse();

    $operator = Operator::factory()->role('Owner')->create();

    $this->actingAs($operator, 'operator')->get(route('admin.overview'))->assertOk();
});

it('sends an operator without two-factor to the setup page once the switch is on', function () {
    AppSettings::set('console.require_2fa', true);

    $operator = Operator::factory()->role('Owner')->create();

    $this->actingAs($operator, 'operator')
        ->get(route('admin.overview'))
        ->assertRedirect(route('admin.two-factor-setup'));
});

it('no longer exempts admin.settings itself — only enrolment', function () {
    // Codex R15 P1b: the earlier shape exempted the whole of admin.settings,
    // on the theory that it was "the page where two-factor is set up". That
    // component also carries staff management, site visibility, network
    // restrictions, account changes, and the two-factor policy switch
    // itself — so an unenrolled operator got unrestricted access to all of
    // that, not just enrolment. An Owner here on purpose: staff management is
    // Owner-only, so this is the account that would have the most to reach.
    AppSettings::set('console.require_2fa', true);

    $operator = Operator::factory()->role('Owner')->create();

    $this->actingAs($operator, 'operator')
        ->get(route('admin.settings'))
        ->assertRedirect(route('admin.two-factor-setup'));
});

it('lets an operator with confirmed two-factor through', function () {
    AppSettings::set('console.require_2fa', true);

    $operator = Operator::factory()->role('Owner')->create([
        'two_factor_secret' => encrypt('JBSWY3DPEHPK3PXP'),
        'two_factor_confirmed_at' => now(),
    ]);

    $this->actingAs($operator, 'operator')->get(route('admin.overview'))->assertOk();
});

it('refuses to switch it on while your own account has no two-factor', function () {
    // The lock-out this exists to prevent: the page that would turn it back off
    // sits behind the switch. Same shape as console.allowed_ips.
    $operator = Operator::factory()->role('Owner')->create();

    Livewire::actingAs($operator, 'operator')->test(Settings::class)
        ->set('requireTwoFactor', true)
        ->call('saveTwoFactorPolicy')
        ->assertHasErrors('requireTwoFactor');

    expect(AppSettings::bool('console.require_2fa', false))->toBeFalse();
});

it('lets an operator without two-factor genuinely enrol while the switch is on, then reach the console', function () {
    // The trap this guards against: the exemption alone is not enough if
    // there is nowhere to actually enrol behind it. Uses a non-Owner role —
    // the requirement applies to every operator, not only the one who can
    // flip the switch.
    AppSettings::set('console.require_2fa', true);

    $operator = Operator::factory()->role('Support')->create(['password' => 'password']);

    // Bounced off an ordinary page…
    $this->actingAs($operator, 'operator')
        ->get(route('admin.overview'))
        ->assertRedirect(route('admin.two-factor-setup'));

    // …and off admin.settings too — that page is no longer the exemption…
    $this->actingAs($operator, 'operator')
        ->get(route('admin.settings'))
        ->assertRedirect(route('admin.two-factor-setup'));

    // …but the two-factor setup page itself is genuinely reachable, not
    // another bounce in disguise.
    $this->actingAs($operator, 'operator')
        ->get(route('admin.two-factor-setup'))
        ->assertOk();

    // Enrol for real: password confirmation, then a real TOTP code.
    $page = Livewire::actingAs($operator, 'operator')
        ->test(TwoFactorSetup::class)
        ->set('confirmablePassword', 'password')
        ->call('confirmPassword')
        ->assertHasNoErrors()
        ->call('enableTwoFactor');

    $code = app(Google2FA::class)->getCurrentOtp(decrypt($operator->refresh()->two_factor_secret));

    $page->set('twoFactorCode', $code)->call('confirmTwoFactor')->assertHasNoErrors();

    expect($operator->refresh()->two_factor_confirmed_at)->not->toBeNull();

    // And now the rest of the console actually opens — admin.settings
    // included, the very page this test proved was blocked above.
    $this->actingAs($operator->fresh(), 'operator')
        ->get(route('admin.overview'))
        ->assertOk();

    $this->actingAs($operator->fresh(), 'operator')
        ->get(route('admin.settings'))
        ->assertOk();
});

it('allows it once your own two-factor is confirmed', function () {
    $operator = Operator::factory()->role('Owner')->create([
        'two_factor_secret' => encrypt('JBSWY3DPEHPK3PXP'),
        'two_factor_confirmed_at' => now(),
    ]);

    Livewire::actingAs($operator, 'operator')->test(Settings::class)
        ->set('requireTwoFactor', true)
        ->call('saveTwoFactorPolicy')
        ->assertHasNoErrors();

    expect(AppSettings::bool('console.require_2fa', false))->toBeTrue();
});
