<?php

namespace Tests\Feature;

use App\Livewire\Files\Index as Files;
use App\Livewire\Servers\Show;
use App\Livewire\Services\Index as Services;
use App\Models\Server;
use App\Models\User;
use App\Services\FirewallService;
use App\Services\FleetService;
use App\Support\Confirm\ConfirmToken;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Mockery;
use Tests\TestCase;

/**
 * Server-scoped confirm tokens must not be retargetable to another host/path.
 * A token sealed for server A, replayed against a handler acting on server B,
 * is a no-op — the destructive SSH/SFTP call never runs.
 */
class ConfirmServerScopeTest extends TestCase
{
    use RefreshDatabase;

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

    private function server(string $name, string $ip): Server
    {
        return Server::create(['name' => $name, 'ip' => $ip, 'ssh_port' => 22, 'status' => 'online']);
    }

    private function confirmed(string $event, array $params, ?int $serverId): string
    {
        $token = ConfirmToken::issue($event, $params, '', null, $serverId);
        ConfirmToken::confirm($token); // the modal confirmation step

        return $token;
    }

    public function test_key_removal_token_for_another_server_is_rejected(): void
    {
        $this->actingAs(User::factory()->create());
        $serverA = $this->server('a', '10.0.0.1');
        $serverB = $this->server('b', '10.0.0.2');

        $fleet = Mockery::mock(FleetService::class);
        $fleet->shouldReceive('removeAuthorizedKey')->never();
        app()->instance(FleetService::class, $fleet);

        $token = $this->confirmed('keyRemoved', ['fingerprint' => 'ab:cd'], $serverA->id);

        // Handler is bound to server B; the token was sealed for A.
        Livewire::test(Show::class, ['server' => $serverB])->call('removeKey', $token);

        $this->assertTrue(true); // Mockery verifies removeAuthorizedKey was never called
    }

    public function test_key_removal_token_for_this_server_proceeds(): void
    {
        $this->actingAs(User::factory()->create());
        $server = $this->server('a', '10.0.0.1');

        $fleet = Mockery::mock(FleetService::class);
        $fleet->shouldReceive('removeAuthorizedKey')->once()
            ->with(Mockery::on(fn ($s) => $s->id === $server->id), 'ab:cd');
        $fleet->shouldReceive('sshKeys')->andReturn([]); // reloadKeys() after success
        app()->instance(FleetService::class, $fleet);

        $token = $this->confirmed('keyRemoved', ['fingerprint' => 'ab:cd'], $server->id);

        Livewire::test(Show::class, ['server' => $server])
            ->call('removeKey', $token)
            ->assertSet('sshKeys', []); // reloadKeys() ran → success path reached
    }

    public function test_file_delete_token_for_another_server_is_rejected(): void
    {
        $this->actingAs(User::factory()->create());
        $serverA = $this->server('a', '10.0.0.1');
        $serverB = $this->server('b', '10.0.0.2');
        session(['active_server_id' => $serverB->id]);

        $fleet = Mockery::mock(FleetService::class);
        $fleet->shouldReceive('files')->andReturn([]); // any dir listing during mount/load
        $fleet->shouldReceive('deleteFile')->never();
        app()->instance(FleetService::class, $fleet);

        $token = $this->confirmed('fileConfirmed', ['path' => '/etc/passwd'], $serverA->id);

        Livewire::test(Files::class)->call('deleteEntry', $token);

        $this->assertTrue(true); // Mockery verifies deleteFile was never called
    }

    public function test_credential_deletion_token_for_another_server_is_rejected(): void
    {
        $this->actingAs(User::factory()->create());
        $serverA = $this->server('a', '10.0.0.1');
        $serverB = $this->server('b', '10.0.0.2');
        $serverB->credential()->create(['username' => 'root', 'auth_type' => 'password', 'secret' => 'x']);

        $token = $this->confirmed('credentialDeleted', [], $serverA->id);

        Livewire::test(Show::class, ['server' => $serverB])->call('deleteCredential', $token);

        $this->assertTrue($serverB->credential()->exists(), "B's credential must survive a server-A token");
    }

    public function test_firewall_rule_deletion_token_for_another_server_is_rejected(): void
    {
        $this->actingAs(User::factory()->create());
        $serverA = $this->server('a', '10.0.0.1');
        $serverB = $this->server('b', '10.0.0.2');

        $firewall = Mockery::mock(FirewallService::class);
        $firewall->shouldReceive('deleteRule')->never();
        app()->instance(FirewallService::class, $firewall);

        $token = $this->confirmed('firewallRuleDelete', ['spec' => '22/tcp', 'label' => 'SSH'], $serverA->id);

        Livewire::test(Show::class, ['server' => $serverB])->call('deleteFirewallRule', $token);

        $this->assertTrue(true); // Mockery verifies deleteRule was never called
    }

    public function test_service_action_token_for_another_server_is_rejected(): void
    {
        $this->actingAs(User::factory()->create());
        $serverA = $this->server('a', '10.0.0.1');
        $serverB = $this->server('b', '10.0.0.2');
        session(['active_server_id' => $serverB->id]);

        $fleet = Mockery::mock(FleetService::class);
        $fleet->shouldReceive('serviceAction')->never();
        app()->instance(FleetService::class, $fleet);

        $token = $this->confirmed('serviceConfirmed', ['op' => 'stop', 'name' => 'nginx.service'], $serverA->id);

        Livewire::test(Services::class)->call('applyService', $token);

        $this->assertTrue(true); // Mockery verifies serviceAction was never called
    }
}
