<?php

use App\Livewire\Admin\ConfirmReleaseBlock;
use App\Livewire\Admin\CustomerDetail;
use App\Livewire\Admin\HostDetail;
use App\Livewire\Admin\Overview;
use App\Models\Customer;
use App\Models\Host;
use App\Models\Instance;
use App\Models\Operator;
use App\Models\SecurityBlock;
use Livewire\Livewire;
use Spatie\Permission\Models\Role;

/**
 * Task 6, Schritt 4: dieselben Sperr-Zeilen in der Konsole, hinter der
 * jeweiligen Berechtigung — hosts.manage auf der Host-Detailseite,
 * instances.manage auf der Kundenseite (Instanz eines Kunden). Beide teilen
 * sich EIN Modal (App\Livewire\Admin\ConfirmReleaseBlock), das nach dem
 * Subjekt der Sperre autorisiert.
 */

// Aufheben ruft HostFirewall::release(), und das verbindet sich per SSH mit
// dem Host. Ohne gebundenen Fake lief der erste Test dieser Datei gegen eine
// echte, nicht erreichbare Adresse und haengte gut zwei Minuten am
// TCP-Verbindungsaufbau — allein, mehr als die gesamte uebrige Suite dieses
// Ordners zusammen.
beforeEach(fn () => app()->instance(
    \App\Services\Ssh\RemoteShell::class,
    new \App\Services\Ssh\FakeRemoteShell
));
it('zeigt einem Betreiber mit hosts.manage die Sperre eines Hosts und laesst ihn sie aufheben', function () {
    $host = Host::factory()->active()->create();
    $block = SecurityBlock::factory()->forHost($host)->create(['ip' => '203.0.113.20']);

    Livewire::actingAs(operator('Admin'), 'operator')
        ->test(HostDetail::class, ['host' => $host])
        ->assertSee('203.0.113.20')
        ->call('onSecurityBlockReleaseConfirmed', $block->uuid)
        ->assertDispatched('notify');

    expect($block->fresh()->released_at)->not->toBeNull();
});

it('verweigert das Aufheben einer Host-Sperre ohne hosts.manage', function () {
    $host = Host::factory()->active()->create();
    $block = SecurityBlock::factory()->forHost($host)->create();

    Livewire::actingAs(operator('Read-only'), 'operator')
        ->test(HostDetail::class, ['host' => $host])
        ->call('onSecurityBlockReleaseConfirmed', $block->uuid)
        ->assertForbidden();

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

it('laesst eine Host-Sperre nicht ueber die Instanz-Seite eines Kunden aufheben', function () {
    // Zwei getrennte Subjekte, zwei getrennte Pruefungen: CustomerDetail darf
    // nur Sperren an EIGENEN Instanzen aufheben, nie eine Host-Sperre — auch
    // nicht mit instances.manage.
    $host = Host::factory()->active()->create();
    $block = SecurityBlock::factory()->forHost($host)->create();
    $customer = Customer::factory()->create();
    Instance::factory()->for($customer)->create();

    Livewire::actingAs(operator('Admin'), 'operator')
        ->test(CustomerDetail::class, ['uuid' => $customer->uuid])
        ->call('onSecurityBlockReleaseConfirmed', $block->uuid);

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

it('zeigt einem Betreiber mit instances.manage die Sperre einer Kunden-Instanz und laesst ihn sie aufheben', function () {
    $customer = Customer::factory()->create();
    $instance = Instance::factory()->for($customer)->create();
    $block = SecurityBlock::factory()->for($instance)->create(['ip' => '198.51.100.42']);

    Livewire::actingAs(operator('Support'), 'operator')
        ->test(CustomerDetail::class, ['uuid' => $customer->uuid])
        ->call('onSecurityBlockReleaseConfirmed', $block->uuid)
        ->assertDispatched('notify');

    expect($block->fresh()->released_at)->not->toBeNull();
});

it('verweigert das Aufheben einer Instanz-Sperre ohne instances.manage', function () {
    // Keine der fuenf mitgelieferten Rollen eignet sich hier: Owner, Admin und
    // Support tragen instances.manage genau wie customers.manage (dieselbe
    // Begruendung wie bei instances.restart), Billing und Read-only haben
    // nicht einmal customers.manage und koennten die Seite gar nicht oeffnen.
    // Also die eigene, engere Rolle, die die Rollen-Konsole genau dafuer
    // vorsieht (Muster: tests/Feature/Admin/RoleManagementTest.php).
    $role = Role::findOrCreate('Kundendienst-eng', 'operator');
    $role->syncPermissions(['console.view', 'customers.manage']);
    $support = Operator::factory()->create(['password' => 'password']);
    $support->assignRole($role);

    $customer = Customer::factory()->create();
    $instance = Instance::factory()->for($customer)->create();
    $block = SecurityBlock::factory()->for($instance)->create();

    Livewire::actingAs($support, 'operator')
        ->test(CustomerDetail::class, ['uuid' => $customer->uuid])
        ->call('onSecurityBlockReleaseConfirmed', $block->uuid)
        ->assertForbidden();

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

it('autorisiert das geteilte Konsolen-Modal nach dem Subjekt der Sperre', function () {
    $host = Host::factory()->active()->create();
    $hostBlock = SecurityBlock::factory()->forHost($host)->create();

    $customer = Customer::factory()->create();
    $instance = Instance::factory()->for($customer)->create();
    $instanceBlock = SecurityBlock::factory()->for($instance)->create();

    // hosts.manage oeffnet die Host-Sperre, aber nicht die Instanz-Sperre.
    Livewire::actingAs(operator('Admin'), 'operator')
        ->test(ConfirmReleaseBlock::class, ['uuid' => $hostBlock->uuid])
        ->assertSee($hostBlock->ip);

    Livewire::actingAs(operator('Read-only'), 'operator')
        ->test(ConfirmReleaseBlock::class, ['uuid' => $instanceBlock->uuid])
        ->assertForbidden();
});

it('zeigt auf der Uebersicht einen Hinweis, solange irgendwo eine Sperre aktiv ist', function () {
    Livewire::actingAs(operator('Owner'), 'operator')
        ->test(Overview::class)
        ->assertDontSee(__('admin.notice.security_blocks', ['n' => 1]));

    $instance = Instance::factory()->create();
    SecurityBlock::factory()->for($instance)->create();

    Livewire::actingAs(operator('Owner'), 'operator')
        ->test(Overview::class)
        ->assertSee(__('admin.notice.security_blocks', ['n' => 1]));
});
