<?php

declare(strict_types=1);

use App\Filament\Resources\BaselineProfileResource;
use App\Filament\Resources\BaselineProfileResource\Pages\ViewBaselineProfile;
use App\Jobs\CaptureBaselineSnapshotJob;
use App\Models\BaselineProfile;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Support\Baselines\BaselineCaptureMode;
use App\Support\OperationRunType;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Queue;
use Livewire\Features\SupportTesting\Testable;
use Livewire\Livewire;

uses(RefreshDatabase::class);

function baselineProfileCaptureHeaderActions(Testable $component): array
{
    $instance = $component->instance();

    if ($instance->getCachedHeaderActions() === []) {
        $instance->cacheInteractsWithHeaderActions();
    }

    return $instance->getCachedHeaderActions();
}

function seedCaptureProfileForTenant(
    ManagedEnvironment $tenant,
    BaselineCaptureMode $captureMode = BaselineCaptureMode::FullContent,
    array $attributes = [],
): BaselineProfile {
    return BaselineProfile::factory()->active()->create(array_merge([
        'workspace_id' => (int) $tenant->workspace_id,
        'capture_mode' => $captureMode->value,
    ], $attributes));
}

it('redirects unauthenticated users (302) when accessing the capture start surface', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $profile = seedCaptureProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);

    $this->get(BaselineProfileResource::getUrl('view', ['record' => $profile], panel: 'admin'))
        ->assertStatus(302);
});

it('returns 404 for authenticated users accessing a baseline profile from another workspace', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    [$otherUser, $otherTenant] = createUserWithTenant(role: 'owner');

    $profile = seedCaptureProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $otherTenant->workspace_id);

    $this->actingAs($otherUser)
        ->get(BaselineProfileResource::getUrl('view', ['record' => $profile], panel: 'admin'))
        ->assertNotFound();
});

it('does not start capture for workspace members missing workspace_baselines.manage', function (): void {
    Queue::fake();

    [$user, $tenant] = createUserWithTenant(role: 'readonly');

    $profile = seedCaptureProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);

    Livewire::actingAs($user)
        ->test(ViewBaselineProfile::class, ['record' => $profile->getKey()])
        ->assertActionVisible('capture')
        ->assertActionHasLabel('capture', 'Capture baseline')
        ->assertActionDisabled('capture')
        ->callAction('capture', data: ['source_environment_id' => (int) $tenant->getKey()])
        ->assertStatus(200);

    Queue::assertNotPushed(CaptureBaselineSnapshotJob::class);
});

it('starts capture successfully for authorized workspace members', function (): void {
    Queue::fake();
    config()->set('tenantpilot.baselines.full_content_capture.enabled', true);

    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $profile = seedCaptureProfileForTenant($tenant);
    $inventorySyncRun = createInventorySyncOperationRunWithCoverage($tenant, [
        'deviceConfiguration' => 'succeeded',
    ]);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);

    $component = Livewire::actingAs($user)
        ->test(ViewBaselineProfile::class, ['record' => $profile->getKey()])
        ->assertActionVisible('capture')
        ->assertActionHasLabel('capture', 'Capture baseline (full content)')
        ->assertActionEnabled('capture')
        ->callAction('capture', data: ['source_environment_id' => (int) $tenant->getKey()])
        ->assertStatus(200);

    $topLevelActionNames = collect(baselineProfileCaptureHeaderActions($component))
        ->reject(static fn ($action): bool => $action instanceof ActionGroup)
        ->filter(static fn ($action): bool => ! method_exists($action, 'isVisible') || $action->isVisible())
        ->map(static fn ($action): ?string => $action instanceof Action ? $action->getName() : null)
        ->filter()
        ->values()
        ->all();

    expect($topLevelActionNames)->toBe(['capture']);

    Queue::assertPushed(CaptureBaselineSnapshotJob::class);

    $run = OperationRun::query()
        ->where('managed_environment_id', (int) $tenant->getKey())
        ->where('type', OperationRunType::BaselineCapture->value)
        ->latest('id')
        ->first();

    expect($run)->not->toBeNull();
    expect($run?->status)->toBe('queued');
    expect(data_get($run?->context, 'baseline_capture.inventory_sync_run_id'))->toBe((int) $inventorySyncRun->getKey());
});

it('shows the shared capture block on the start surface when no credible inventory basis exists', function (): void {
    Queue::fake();

    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $profile = seedCaptureProfileForTenant($tenant, BaselineCaptureMode::Opportunistic, [
        'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []],
    ]);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);

    Livewire::actingAs($user)
        ->test(ViewBaselineProfile::class, ['record' => $profile->getKey()])
        ->assertActionVisible('capture')
        ->assertActionHasLabel('capture', 'Capture baseline')
        ->assertActionEnabled('capture')
        ->callAction('capture', data: ['source_environment_id' => (int) $tenant->getKey()])
        ->assertNotified('Cannot start capture')
        ->assertStatus(200);

    Queue::assertNotPushed(CaptureBaselineSnapshotJob::class);
    expect(OperationRun::query()->where('type', OperationRunType::BaselineCapture->value)->count())->toBe(0);
});

it('does not start full-content capture when rollout is disabled', function (): void {
    Queue::fake();
    config()->set('tenantpilot.baselines.full_content_capture.enabled', false);

    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $profile = seedCaptureProfileForTenant($tenant);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);

    Livewire::actingAs($user)
        ->test(ViewBaselineProfile::class, ['record' => $profile->getKey()])
        ->assertActionVisible('capture')
        ->assertActionHasLabel('capture', 'Capture baseline (full content)')
        ->assertActionEnabled('capture')
        ->callAction('capture', data: ['source_environment_id' => (int) $tenant->getKey()])
        ->assertNotified('Cannot start capture')
        ->assertStatus(200);

    Queue::assertNotPushed(CaptureBaselineSnapshotJob::class);
    expect(OperationRun::query()->where('type', OperationRunType::BaselineCapture->value)->count())->toBe(0);
});

it('shows readiness copy without exposing raw canonical scope json on the capture start surface', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $profile = seedCaptureProfileForTenant($tenant, BaselineCaptureMode::Opportunistic, [
        'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []],
    ]);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);

    Livewire::actingAs($user)
        ->test(ViewBaselineProfile::class, ['record' => $profile->getKey()])
        ->assertSee('Support readiness')
        ->assertSee('Capture: ready. Compare: ready.')
        ->assertDontSee('subject_type_keys')
        ->assertDontSee('canonical_scope');
});

it('does not start capture when the stored canonical scope is invalid', function (): void {
    Queue::fake();

    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $profile = seedCaptureProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);

    DB::table('baseline_profiles')
        ->where('id', (int) $profile->getKey())
        ->update([
            'scope_jsonb' => json_encode([
                'version' => 2,
                'entries' => [
                    [
                        'domain_key' => 'platform_foundation',
                        'subject_class' => 'configuration_resource',
                        'subject_type_keys' => ['intuneRoleAssignment'],
                        'filters' => [],
                    ],
                ],
            ], JSON_THROW_ON_ERROR),
            'updated_at' => now(),
        ]);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);

    Livewire::actingAs($user)
        ->test(ViewBaselineProfile::class, ['record' => $profile->getKey()])
        ->assertActionVisible('capture')
        ->callAction('capture', data: ['source_environment_id' => (int) $tenant->getKey()])
        ->assertNotified('Cannot start capture')
        ->assertStatus(200);

    Queue::assertNotPushed(CaptureBaselineSnapshotJob::class);
    expect(OperationRun::query()->where('type', OperationRunType::BaselineCapture->value)->count())->toBe(0);
});
