<?php

use App\Livewire\Admin\Integrations;
use App\Livewire\Settings;
use App\Models\Operator;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Livewire\Livewire;

beforeEach(fn () => config()->set('admin_access.secrets_key', 'base64:'.base64_encode(random_bytes(32))));

it('confirms a console password against the operator record, not a portal user', function () {
    $operator = Operator::factory()->role('Owner')->create(['password' => 'operator-passwort']);

    Livewire::actingAs($operator, 'operator')->test(Integrations::class)
        ->set('confirmablePassword', 'operator-passwort')
        ->call('confirmPassword')
        ->assertHasNoErrors();
});

it('rejects a password that belongs to a portal user with the same address', function () {
    // The trap: two tables, one address. Confirming against the wrong one would
    // let a customer's password unlock the console.
    $operator = Operator::factory()->role('Owner')->create([
        'email' => 'doppelt@clupilot.test', 'password' => 'operator-passwort',
    ]);
    User::factory()->create(['email' => 'doppelt@clupilot.test', 'password' => 'kunden-passwort']);

    Livewire::actingAs($operator, 'operator')->test(Integrations::class)
        ->set('confirmablePassword', 'kunden-passwort')
        ->call('confirmPassword')
        ->assertHasErrors('confirmablePassword');
});

it('still confirms a portal password on the web guard', function () {
    $user = User::factory()->create(['password' => 'kunden-passwort']);

    Livewire::actingAs($user)->test(Settings::class)
        ->set('confirmablePassword', 'kunden-passwort')
        ->call('confirmPassword')
        ->assertHasNoErrors();
});

it('resolves the operator explicitly by guard, not by whichever guard happens to be the ambient default', function () {
    // actingAs()/Livewire::actingAs() both call Auth::shouldUse($guard) as a
    // side effect, which makes the named guard the DEFAULT guard — and under
    // that, auth()->user() and auth()->guard('operator')->user() resolve
    // identically, so every OTHER test in this file passes just the same
    // whether confirmPassword() asks for the right guard explicitly or just
    // reads the ambient default. That is not a hypothetical: reverting the
    // trait's guard(...)->user() call back to a bare auth()->user() passes
    // the entire 834-test suite, this file included — the gap this test
    // closes, by making that distinction observable.
    //
    // Signing in with Auth::guard('operator')->login() alone (no actingAs)
    // does NOT reach this test: it proves confirmationGuard is genuinely
    // consulted, but Integrations::mount()'s own $this->authorize('secrets.manage')
    // ALSO reads the ambient default guard (Laravel's Gate resolves the
    // current user the same bare way) — so mount() itself 403s before
    // confirmPassword() is ever reached, on the very account this test means
    // to prove works. Confirmed directly: Auth::guard('web')->check() is
    // false, Auth::guard('operator')->check() is true, and still
    // Gate::check('secrets.manage') is false in that state.
    //
    // So: mount through actingAs() as usual — authorize() runs exactly once,
    // while shouldUse('operator') is still in effect from actingAs, and
    // legitimately passes. mount() does not run again on a later ->call().
    // Only THEN, before touching confirmPassword() specifically, undo the
    // masking by hand: Auth::shouldUse('web') switches the ambient default
    // back to 'web' — genuinely empty, not merely relabelled — while the
    // operator's own session on the 'operator' guard is untouched (setUser()
    // on a guard does not depend on which guard is "default"). That is
    // exactly the real shape of a console request: a genuine operator sign-in
    // (App\Livewire\Auth\OperatorLogin::completeLogin()) never touches the
    // web guard, default or not.
    $operator = Operator::factory()->role('Owner')->create(['password' => 'operator-passwort']);

    $page = Livewire::actingAs($operator, 'operator')->test(Integrations::class);

    Auth::shouldUse('web');

    expect(Auth::guard('web')->check())->toBeFalse()
        ->and(Auth::guard('operator')->check())->toBeTrue()
        ->and(auth()->check())->toBeFalse();

    $page->set('confirmablePassword', 'operator-passwort')
        ->call('confirmPassword')
        ->assertHasNoErrors();
});

it("does not let a portal customer's password confirmation satisfy the console's own gate", function () {
    // Codex R15 P1a: confirmPassword() always checked the RIGHT account per
    // guard (the tests above), but until now it stamped a single
    // guard-agnostic session marker — auth.password_confirmed_at, no guard or
    // identity in the key — that ANY confirmationGuard() then accepted. In
    // shared-host mode (this suite's default, and this VM's own mode) the
    // portal and console guards keep their login key in the SAME session, so
    // signing into both and confirming the CUSTOMER's password on the portal
    // settings page left the marker in place for the OPERATOR-only
    // Integrations page too. This is the escalation itself, not a proxy for it.
    $operator = Operator::factory()->role('Owner')->create(['password' => 'operator-passwort']);
    $customer = User::factory()->create(['password' => 'kunden-passwort']);

    // Signed into both guards, one shared session — the login() calls write
    // real session data, unlike actingAs() (in-memory user only), so the
    // marker written by the first call below is genuinely there for the
    // second to find, exactly as it would be for a real browser carrying
    // both identities.
    Auth::guard('web')->login($customer);
    Auth::guard('operator')->login($operator);

    // Confirm the CUSTOMER's password on the portal settings page.
    // App\Livewire\Settings::confirmationGuard() defaults to 'web', resolved
    // explicitly by guard — no actingAs needed to reach the right account,
    // the login() call above already put it in the session.
    Livewire::test(Settings::class)
        ->set('confirmablePassword', 'kunden-passwort')
        ->call('confirmPassword')
        ->assertHasNoErrors();

    // Walk into the console as the OPERATOR with that confirmation still
    // sitting in the session. actingAs() only sets the AMBIENT default guard
    // Gate reads for Integrations::mount()'s authorize() call — it does not touch
    // the 'web' guard's own session entry set above, so this is the real
    // shape of the attack: one browser, two identities, one session.
    Livewire::actingAs($operator, 'operator')->test(Integrations::class)
        ->assertViewHas('unlocked', false);
});
