<?php // tests/Feature/Security/SecurityPageTest.php

use App\Livewire\Security;
use App\Models\Customer;
use App\Models\Instance;
use App\Models\SecurityBlock;
use Livewire\Livewire;

// Abweichung vom Auftragszettel: Customer::factory()->create() verknuepft
// keinen User (user_id bleibt NULL, siehe CustomerFactory) — jeder bestehende
// Test, der einen Kunden anmeldet, ruft dafuer ensureUser() (Muster:
// tests/Feature/CustomerTwoFactorTest.php::portalUser()). Der RUECKGABEWERT
// wird gebraucht, nicht $customer->user danach: ensureUser() selbst liest
// $this->user als ersten Schritt ("if ($this->user) return $this->user;"),
// bevor user_id gesetzt ist — das cacht die Beziehung als null, und ein
// spaeteres update() raeumt einen bereits geladenen Beziehungs-Cache nicht
// weg. $customer->user bliebe also null, obwohl user_id laengst gesetzt ist.

it('zeigt dem Inhaber die Sperren seiner eigenen Instanz', function () {
    $customer = Customer::factory()->create();
    $user = $customer->ensureUser();
    $instance = Instance::factory()->for($customer)->create();
    $block = SecurityBlock::factory()->for($instance)->create(['ip' => '203.0.113.7']);

    Livewire::actingAs($user)->test(Security::class)->assertSee('203.0.113.7');
});

it('zeigt einem Inhaber die Sperren eines FREMDEN Kunden nicht', function () {
    $meine = Customer::factory()->create();
    $user = $meine->ensureUser();
    $fremde = Instance::factory()->create();
    SecurityBlock::factory()->for($fremde)->create(['ip' => '198.51.100.9']);

    Livewire::actingAs($user)->test(Security::class)->assertDontSee('198.51.100.9');
});

it('laesst einen Inhaber eine fremde Sperre nicht aufheben', function () {
    $meine = Customer::factory()->create();
    $user = $meine->ensureUser();
    $fremd = SecurityBlock::factory()->for(Instance::factory()->create())->create();

    Livewire::actingAs($user)->test(Security::class)
        ->call('onReleaseConfirmed', $fremd->uuid)
        ->assertForbidden();

    expect($fremd->fresh()->released_at)->toBeNull();
});

// Der Weg, den ein Mensch nimmt: das Modal mutiert nichts, es schickt nur
// 'block-release-confirmed' (R23). Die bestehenden Pruefungen rufen
// onReleaseConfirmed() direkt auf — damit steht das Ereignis selbst nie auf
// dem Pruefstand, und die Zeile danach auch nicht. Genau das war der Verdacht:
// dass die Zeile nach dem Aufheben weiter auf "Aktiv" steht, bis jemand neu
// laedt. Diese Pruefung nimmt den ganzen Weg.
it('raeumt die Zeile nach dem Aufheben aus der aktiven Liste in den Verlauf', function () {
    $customer = Customer::factory()->create();
    $user = $customer->ensureUser();
    $instance = Instance::factory()->for($customer)->create();
    $block = SecurityBlock::factory()->for($instance)->create(['ip' => '203.0.113.7']);

    $seite = Livewire::actingAs($user)->test(Security::class)
        ->assertSee('active-'.$block->uuid, escape: false)
        ->assertDontSee('history-'.$block->uuid, escape: false);

    $seite->dispatch('block-release-confirmed', uuid: $block->uuid)
        ->assertDontSee('active-'.$block->uuid, escape: false)
        ->assertSee('history-'.$block->uuid, escape: false)
        ->assertSee(__('security.status_released'));
});

it('sagt es, wenn die Firewall beim Aufheben nicht mitkam', function () {
    // HostFirewall schluckt jeden Fehlschlag und meldet ihn nur ins Log. Ohne
    // diesen Unterschied faende der Kunde „Sperre aufgehoben." vor, versuchte
    // sich gleich wieder anzumelden — und wuerde wortlos erneut abgewiesen,
    // ohne dass ihm irgendjemand sagen kann, warum.
    $shell = new \App\Services\Ssh\FakeRemoteShell;
    $shell->failConnect = true;
    app()->instance(\App\Services\Ssh\RemoteShell::class, $shell);

    $customer = Customer::factory()->create();
    $user = $customer->ensureUser();
    $host = \App\Models\Host::factory()->active()->create(['ssh_host_key' => 'SHA256:abc']);
    $instance = Instance::factory()->for($customer)->create(['host_id' => $host->id]);
    $block = SecurityBlock::factory()->for($instance)->create();

    Livewire::actingAs($user)->test(Security::class)
        ->call('onReleaseConfirmed', $block->uuid)
        ->assertDispatched('notify', message: __('security.blocks_released_pending'));

    // Der Datensatz steht trotzdem — er IST die Sperre, die Firewall-Menge nur
    // ihr Abdruck. ScanForIntrusions traegt das Loeschen nach.
    expect($block->fresh()->released_at)->not->toBeNull();
});

it('hebt eine eigene Sperre auf und traegt ein, wer es war', function () {
    $customer = Customer::factory()->create();
    $user = $customer->ensureUser();
    $instance = Instance::factory()->for($customer)->create();
    $block = SecurityBlock::factory()->for($instance)->create();

    Livewire::actingAs($user)->test(Security::class)
        ->call('onReleaseConfirmed', $block->uuid);

    expect($block->fresh()->released_at)->not->toBeNull()
        ->and($block->fresh()->released_by_id)->toBe($user->id);
});
