<?php

declare(strict_types=1);

use App\Filament\Resources\AlertDestinationResource;
use App\Filament\Resources\AlertDestinationResource\Pages\EditAlertDestination;
use App\Jobs\Alerts\DeliverAlertsJob;
use App\Models\AlertDelivery;
use App\Models\AlertDestination;
use App\Models\AuditLog;
use App\Models\Workspace;
use App\Support\Audit\AuditActionId;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
use Livewire\Livewire;

uses(RefreshDatabase::class);

// ---------------------------------------------------------------------------
// T007 — Happy path: owner sends a test message, delivery + job created
// ---------------------------------------------------------------------------
it('creates a test delivery and dispatches a deliver job on send test message', function (): void {
    Queue::fake();

    [$user] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $workspaceId = (int) session()->get(WorkspaceContext::SESSION_KEY);
    $destination = AlertDestination::factory()->create([
        'workspace_id' => $workspaceId,
        'is_enabled' => true,
    ]);

    Livewire::test(EditAlertDestination::class, ['record' => $destination->getRouteKey()])
        ->callAction('send_test_message');

    $delivery = AlertDelivery::query()
        ->where('alert_destination_id', $destination->getKey())
        ->where('event_type', AlertDelivery::EVENT_TYPE_TEST)
        ->first();

    expect($delivery)->not->toBeNull();
    expect($delivery->workspace_id)->toBe($workspaceId);
    expect($delivery->managed_environment_id)->toBeNull();
    expect($delivery->alert_rule_id)->toBeNull();
    expect($delivery->status)->toBe(AlertDelivery::STATUS_QUEUED);

    Queue::assertPushed(DeliverAlertsJob::class, function (DeliverAlertsJob $job) use ($workspaceId): bool {
        return $job->workspaceId === $workspaceId;
    });
});

// ---------------------------------------------------------------------------
// T008 — Rate-limit refusal: second request within 60 seconds is rejected
// ---------------------------------------------------------------------------
it('refuses a second test message within 60 seconds', function (): void {
    Queue::fake();

    [$user] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $workspaceId = (int) session()->get(WorkspaceContext::SESSION_KEY);
    $destination = AlertDestination::factory()->create([
        'workspace_id' => $workspaceId,
        'is_enabled' => true,
    ]);

    AlertDelivery::factory()->test()->create([
        'workspace_id' => $workspaceId,
        'alert_destination_id' => (int) $destination->getKey(),
        'created_at' => now()->subSeconds(30),
    ]);

    Livewire::test(EditAlertDestination::class, ['record' => $destination->getRouteKey()])
        ->callAction('send_test_message')
        ->assertNotified();

    expect(
        AlertDelivery::query()
            ->where('alert_destination_id', $destination->getKey())
            ->where('event_type', AlertDelivery::EVENT_TYPE_TEST)
            ->count()
    )->toBe(1);

    Queue::assertNothingPushed();
});

// ---------------------------------------------------------------------------
// T009 — Authorization: readonly member is forbidden from edit page
// ---------------------------------------------------------------------------
it('forbids readonly members from accessing the edit page for destinations', function (): void {
    Queue::fake();

    [$user] = createUserWithTenant(role: 'readonly');
    $this->actingAs($user);

    $workspaceId = (int) session()->get(WorkspaceContext::SESSION_KEY);
    $destination = AlertDestination::factory()->create([
        'workspace_id' => $workspaceId,
        'is_enabled' => true,
    ]);

    $this->get(AlertDestinationResource::getUrl('edit', ['record' => $destination], panel: 'admin'))
        ->assertForbidden();

    Queue::assertNothingPushed();
});

// ---------------------------------------------------------------------------
// T010 — Non-member: denied as not found (404)
// ---------------------------------------------------------------------------
it('returns 404 for non-member trying to access edit page with send test action', function (): void {
    [$user] = createUserWithTenant(role: 'owner');

    $otherWorkspace = Workspace::factory()->create();
    $destination = AlertDestination::factory()->create([
        'workspace_id' => (int) $otherWorkspace->getKey(),
        'is_enabled' => true,
    ]);

    $this->actingAs($user)
        ->get(AlertDestinationResource::getUrl('edit', ['record' => $destination], panel: 'admin'))
        ->assertNotFound();
});

// ---------------------------------------------------------------------------
// T011 — Audit log assertion: test request is audit-logged
// ---------------------------------------------------------------------------
it('creates an audit log entry when a test message is sent', function (): void {
    Queue::fake();

    [$user] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $workspaceId = (int) session()->get(WorkspaceContext::SESSION_KEY);
    $destination = AlertDestination::factory()->create([
        'workspace_id' => $workspaceId,
        'is_enabled' => true,
    ]);

    Livewire::test(EditAlertDestination::class, ['record' => $destination->getRouteKey()])
        ->callAction('send_test_message');

    $auditLog = AuditLog::query()
        ->where('workspace_id', $workspaceId)
        ->where('action', AuditActionId::AlertDestinationTestRequested->value)
        ->where('resource_type', 'alert_destination')
        ->where('resource_id', (string) $destination->getKey())
        ->first();

    expect($auditLog)->not->toBeNull();
    expect($auditLog->actor_id)->toBe((int) $user->getKey());
});

// ---------------------------------------------------------------------------
// T012 — Confirmation requirement: action requires confirmation before execution
// ---------------------------------------------------------------------------
it('requires confirmation before sending a test message', function (): void {
    Queue::fake();

    [$user] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $workspaceId = (int) session()->get(WorkspaceContext::SESSION_KEY);
    $destination = AlertDestination::factory()->create([
        'workspace_id' => $workspaceId,
        'is_enabled' => true,
    ]);

    Livewire::test(EditAlertDestination::class, ['record' => $destination->getRouteKey()])
        ->assertActionExists('send_test_message')
        ->mountAction('send_test_message')
        ->assertActionMounted('send_test_message');

    expect(AlertDelivery::query()->count())->toBe(0);
    Queue::assertNothingPushed();
});
