<?php

namespace Tests\Feature;

use App\Livewire\Servers\Groups;
use App\Livewire\Servers\Index as ServersIndex;
use App\Models\Server;
use App\Models\ServerGroup;
use App\Models\User;
use App\Support\Confirm\ConfirmToken;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;

class ServerGroupsComponentTest extends TestCase
{
    use RefreshDatabase;

    private function admin(): User
    {
        return User::factory()->create(['must_change_password' => false]);
    }

    private function operator(): User
    {
        return User::factory()->operator()->create(['must_change_password' => false]);
    }

    private function viewer(): User
    {
        return User::factory()->viewer()->create(['must_change_password' => false]);
    }

    // ── route gating (manage-fleet = admin only) ────────────────────────────────

    public function test_viewer_cannot_open_the_groups_page(): void
    {
        $this->actingAs($this->viewer())->get('/servers/groups')->assertForbidden();
    }

    public function test_operator_cannot_open_the_groups_page(): void
    {
        $this->actingAs($this->operator())->get('/servers/groups')->assertForbidden();
    }

    public function test_admin_can_open_the_groups_page(): void
    {
        $this->actingAs($this->admin())->get('/servers/groups')->assertOk();
    }

    // ── create ──────────────────────────────────────────────────────────────────

    public function test_admin_creates_a_group_and_it_is_audited(): void
    {
        $this->actingAs($this->admin());

        Livewire::test(Groups::class)
            ->set('name', 'Produktion')
            ->set('color', 'online')
            ->call('create')
            ->assertOk()
            ->assertHasNoErrors();

        $this->assertDatabaseHas('server_groups', ['name' => 'Produktion', 'color' => 'online']);
        $this->assertDatabaseHas('audit_events', ['action' => 'group.create', 'target' => 'Produktion']);
    }

    public function test_create_rejects_a_duplicate_name(): void
    {
        $this->actingAs($this->admin());
        ServerGroup::create(['name' => 'prod', 'color' => 'accent']);

        Livewire::test(Groups::class)
            ->set('name', 'prod')
            ->call('create')
            ->assertHasErrors('name');

        $this->assertSame(1, ServerGroup::where('name', 'prod')->count());
    }

    public function test_create_rejects_an_unknown_color_token(): void
    {
        $this->actingAs($this->admin());

        Livewire::test(Groups::class)
            ->set('name', 'x')
            ->set('color', 'rebeccapurple')
            ->call('create')
            ->assertHasErrors('color');

        $this->assertDatabaseCount('server_groups', 0);
    }

    // ── rename / recolour / membership ──────────────────────────────────────────

    public function test_admin_renames_and_recolors_a_group(): void
    {
        $this->actingAs($this->admin());
        $g = ServerGroup::create(['name' => 'old', 'color' => 'accent']);

        Livewire::test(Groups::class)
            ->call('rename', $g->uuid, 'Neu')
            ->call('setColor', $g->uuid, 'cyan');

        $this->assertDatabaseHas('server_groups', ['id' => $g->id, 'name' => 'Neu', 'color' => 'cyan']);
    }

    public function test_set_color_ignores_an_unknown_token(): void
    {
        $this->actingAs($this->admin());
        $g = ServerGroup::create(['name' => 'g', 'color' => 'accent']);

        Livewire::test(Groups::class)->call('setColor', $g->uuid, 'rebeccapurple');

        $this->assertSame('accent', $g->fresh()->color); // unchanged
    }

    public function test_toggle_member_adds_then_removes_a_server(): void
    {
        $this->actingAs($this->admin());
        $g = ServerGroup::create(['name' => 'g', 'color' => 'accent']);
        $s = Server::create(['name' => 's', 'ip' => '10.0.0.1', 'ssh_port' => 22, 'status' => 'online']);

        Livewire::test(Groups::class)->call('toggleMember', $g->uuid, $s->uuid);
        $this->assertTrue($g->servers()->where('servers.id', $s->id)->exists());

        Livewire::test(Groups::class)->call('toggleMember', $g->uuid, $s->uuid);
        $this->assertFalse($g->servers()->where('servers.id', $s->id)->exists());
    }

    // ── delete via signed confirm token ─────────────────────────────────────────

    public function test_admin_deletes_a_group_via_a_confirmed_token(): void
    {
        $this->actingAs($this->admin());
        $g = ServerGroup::create(['name' => 'gone', 'color' => 'offline']);

        $token = ConfirmToken::issue('groupConfirmed', ['uuid' => $g->uuid], 'group.delete', $g->name, null);
        ConfirmToken::confirm($token); // the modal confirmation step

        Livewire::test(Groups::class)
            ->call('deleteGroup', $token)
            ->assertOk();

        $this->assertDatabaseMissing('server_groups', ['id' => $g->id]);
    }

    public function test_a_forged_delete_token_is_a_no_op(): void
    {
        $this->actingAs($this->admin());
        $g = ServerGroup::create(['name' => 'stay', 'color' => 'accent']);

        Livewire::test(Groups::class)->call('deleteGroup', 'not-a-real-token');

        $this->assertDatabaseHas('server_groups', ['id' => $g->id]);
    }

    // ── fleet filter (Servers\Index) — open to any role ─────────────────────────

    public function test_group_filter_shows_only_members(): void
    {
        $this->actingAs($this->viewer()); // filtering is open to everyone
        $g = ServerGroup::create(['name' => 'web', 'color' => 'cyan']);
        $member = Server::create(['name' => 'web1', 'ip' => '10.0.0.1', 'ssh_port' => 22, 'status' => 'online']);
        $other = Server::create(['name' => 'db1', 'ip' => '10.0.0.2', 'ssh_port' => 22, 'status' => 'online']);
        $g->servers()->attach($member->id);

        Livewire::test(ServersIndex::class)
            ->set('group', $g->uuid)
            ->assertSee('web1')
            ->assertDontSee('db1');
    }

    public function test_unknown_group_filter_falls_back_to_all(): void
    {
        $this->actingAs($this->viewer());
        Server::create(['name' => 'srv', 'ip' => '10.0.0.1', 'ssh_port' => 22, 'status' => 'online']);

        Livewire::test(ServersIndex::class)
            ->set('group', 'no-such-uuid')
            ->assertOk()
            ->assertSee('srv'); // not filtered out by a bogus group
    }
}
