<?php

namespace Tests\Feature;

use App\Livewire\Certs\Index;
use App\Models\CertEndpoint;
use App\Models\User;
use App\Services\CertService;
use App\Support\Confirm\ConfirmToken;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Mockery;
use Tests\TestCase;

class CertsComponentTest extends TestCase
{
    use RefreshDatabase;

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

    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]);
    }

    public function test_viewer_cannot_open_certs(): void
    {
        $this->actingAs($this->viewer())->get('/certs')->assertForbidden();
    }

    public function test_operator_can_open_certs(): void
    {
        $this->actingAs($this->operator())->get('/certs')->assertOk();
    }

    public function test_operator_adds_an_endpoint_and_it_is_audited(): void
    {
        $this->actingAs($this->operator());

        Livewire::test(Index::class)
            ->set('label', 'Panel')
            ->set('host', 'panel.example.com')
            ->set('port', 443)
            ->call('addEndpoint')
            ->assertHasNoErrors();

        $this->assertDatabaseHas('cert_endpoints', ['host' => 'panel.example.com', 'port' => 443]);
        $this->assertDatabaseHas('audit_events', ['action' => 'cert.endpoint_add']);
    }

    public function test_add_rejects_a_host_with_a_scheme_or_metacharacters(): void
    {
        $this->actingAs($this->operator());

        Livewire::test(Index::class)
            ->set('host', 'https://evil.com/;rm -rf')
            ->set('port', 443)
            ->call('addEndpoint')
            ->assertHasErrors('host');

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

    public function test_add_rejects_an_out_of_range_port(): void
    {
        $this->actingAs($this->operator());

        Livewire::test(Index::class)
            ->set('host', 'ok.example.com')
            ->set('port', 99999)
            ->call('addEndpoint')
            ->assertHasErrors('port');
    }

    public function test_scan_checks_each_endpoint(): void
    {
        $this->actingAs($this->operator());
        CertEndpoint::create(['host' => 'svc.example.com', 'port' => 443]);

        $certs = Mockery::mock(CertService::class);
        $certs->shouldReceive('check')->once()->andReturn(['ok' => true, 'subject' => 'svc.example.com', 'issuer' => 'LE', 'expiresAt' => time() + 5 * 86400, 'daysLeft' => 5]);
        $certs->shouldReceive('status')->once()->with(5)->andReturn('critical');
        app()->instance(CertService::class, $certs);

        Livewire::test(Index::class)
            ->call('scan')
            ->assertOk()
            ->assertSet('ready', true)
            ->assertSee(__('certs.status_critical'));
    }

    public function test_operator_deletes_an_endpoint_via_a_confirmed_token(): void
    {
        $this->actingAs($this->operator());
        $ep = CertEndpoint::create(['host' => 'gone.example.com', 'port' => 443]);
        $token = ConfirmToken::issue('certEndpointDeleted', ['uuid' => $ep->uuid], 'cert.endpoint_delete', $ep->host, null);
        ConfirmToken::confirm($token);

        Livewire::test(Index::class)->call('deleteEndpoint', $token)->assertOk();

        $this->assertDatabaseMissing('cert_endpoints', ['id' => $ep->id]);
    }
}
