<?php

require_once dirname(__DIR__).'/Baselines/Support/FakeCompareStrategy.php';

use App\Filament\Pages\BaselineCompareLanding;
use App\Filament\Resources\BaselineProfileResource;
use App\Jobs\CompareBaselineToTenantJob;
use App\Livewire\BulkOperationProgress;
use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\BaselineTenantAssignment;
use App\Models\OperationRun;
use App\Support\Baselines\BaselineReasonCodes;
use App\Support\Baselines\Compare\CompareStrategyRegistry;
use App\Support\Baselines\Compare\IntuneCompareStrategy;
use App\Support\Governance\GovernanceSubjectTaxonomyRegistry;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
use App\Support\OpsUx\OpsUxBrowserEvents;
use Filament\Actions\Action;
use Filament\Facades\Filament;
use Illuminate\Support\Facades\Queue;
use Tests\Feature\Baselines\Support\FakeCompareStrategy;
use Tests\Feature\Baselines\Support\FakeGovernanceSubjectTaxonomyRegistry;

it('redirects unauthenticated users (302)', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $this->get(BaselineCompareLanding::getUrl(tenant: $tenant, panel: 'admin'))
        ->assertStatus(302);
});

it('returns 404 for authenticated users not entitled to the tenant', function (): void {
    [$member, $tenant] = createUserWithTenant(role: 'owner');
    $nonMember = \App\Models\User::factory()->create();

    $this->actingAs($nonMember)
        ->get(BaselineCompareLanding::getUrl(tenant: $tenant, panel: 'admin'))
        ->assertNotFound();
});

it('does not start baseline compare for members missing tenant.sync', function (): void {
    Queue::fake();

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

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $tenant->workspace_id,
    ]);

    $snapshot = BaselineSnapshot::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);

    BaselineTenantAssignment::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    baselineCompareLandingLivewire($tenant)
        ->assertActionVisible('compareNow')
        ->assertActionDisabled('compareNow')
        ->assertDontSee('Monitoring landing')
        ->assertDontSee('Navigation lane')
        ->callAction('compareNow')
        ->assertStatus(200);

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

it('dispatches ops-ux run-enqueued after starting baseline compare', function (): void {
    Queue::fake();
    config()->set('tenantpilot.baselines.full_content_capture.enabled', true);

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

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'capture_mode' => \App\Support\Baselines\BaselineCaptureMode::FullContent->value,
    ]);

    $snapshot = BaselineSnapshot::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);

    BaselineTenantAssignment::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    baselineCompareLandingLivewire($tenant)
        ->assertActionHasLabel('compareNow', 'Compare now (full content)')
        ->callAction('compareNow')
        ->assertDispatchedTo(BulkOperationProgress::class, OpsUxBrowserEvents::RunEnqueued, tenantId: (int) $tenant->getKey());

    Queue::assertPushed(CompareBaselineToTenantJob::class);

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

    expect($run)->not->toBeNull();
    expect($run?->status)->toBe('queued');
});

it('keeps compare-now confirmation and mutation-scope messaging unchanged on the landing surface', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'capture_mode' => \App\Support\Baselines\BaselineCaptureMode::FullContent->value,
    ]);

    $snapshot = BaselineSnapshot::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);

    BaselineTenantAssignment::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    baselineCompareLandingLivewire($tenant)
        ->assertActionExists('compareNow', function (Action $action): bool {
            return $action->getLabel() === 'Compare now (full content)'
                && $action->isConfirmationRequired()
                && $action->getModalDescription() === 'This will refresh content evidence on demand (redacted) before comparing the current tenant inventory against the assigned baseline snapshot.';
        });
});

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

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

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'capture_mode' => \App\Support\Baselines\BaselineCaptureMode::FullContent->value,
    ]);

    $snapshot = BaselineSnapshot::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);

    BaselineTenantAssignment::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    baselineCompareLandingLivewire($tenant)
        ->assertActionHasLabel('compareNow', 'Compare now (full content)')
        ->assertActionEnabled('compareNow')
        ->callAction('compareNow')
        ->assertNotified('Cannot start comparison')
        ->assertStatus(200);

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

it('shows mixed-strategy compare rejection truth on the tenant landing surface', function (): void {
    Queue::fake();

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

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    app()->instance(GovernanceSubjectTaxonomyRegistry::class, new FakeGovernanceSubjectTaxonomyRegistry);
    app()->instance(CompareStrategyRegistry::class, new CompareStrategyRegistry([
        app(IntuneCompareStrategy::class),
        app(FakeCompareStrategy::class),
    ]));

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'scope_jsonb' => [
            'version' => 2,
            'entries' => [
                [
                    'domain_key' => 'intune',
                    'subject_class' => 'policy',
                    'subject_type_keys' => ['deviceConfiguration'],
                    'filters' => [],
                ],
                [
                    'domain_key' => 'entra',
                    'subject_class' => 'control',
                    'subject_type_keys' => ['conditionalAccessPolicy'],
                    'filters' => [],
                ],
            ],
        ],
    ]);

    $snapshot = BaselineSnapshot::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);

    BaselineTenantAssignment::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    baselineCompareLandingLivewire($tenant)
        ->callAction('compareNow')
        ->assertNotified('Cannot start comparison')
        ->assertStatus(200);

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

it('can refresh stats without calling mount directly', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    baselineCompareLandingLivewire($tenant)
        ->call('refreshStats')
        ->assertStatus(200);
});

it('exposes a compare-matrix handoff that preserves carried subject focus from launch context', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $tenant->workspace_id,
    ]);

    $snapshot = BaselineSnapshot::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);

    BaselineTenantAssignment::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $component = baselineCompareLandingLivewire($tenant, [
        'baseline_profile_id' => (int) $profile->getKey(),
        'subject_key' => 'wifi-corp-profile',
    ]);

    expect($component->instance()->openCompareMatrixUrl())
        ->toBe(BaselineProfileResource::compareMatrixUrl($profile).'?subject_key=wifi-corp-profile');

    $component->assertSee('Open compare matrix');
});

it('exposes full coverage + fidelity context in stats', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $tenant->workspace_id,
    ]);

    $snapshot = BaselineSnapshot::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);

    BaselineTenantAssignment::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $compareRun = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => OperationRunType::BaselineCompare->value,
        'status' => OperationRunStatus::Completed->value,
        'outcome' => OperationRunOutcome::Succeeded->value,
        'completed_at' => now(),
        'context' => [
            'baseline_profile_id' => (int) $profile->getKey(),
            'baseline_snapshot_id' => (int) $snapshot->getKey(),
            'baseline_compare' => [
                'coverage' => [
                    'effective_types' => ['deviceConfiguration'],
                    'covered_types' => ['deviceConfiguration'],
                    'uncovered_types' => [],
                    'proof' => true,
                ],
                'fidelity' => 'meta',
            ],
        ],
    ]);

    baselineCompareLandingLivewire($tenant)
        ->call('refreshStats')
        ->assertSet('operationRunId', (int) $compareRun->getKey())
        ->assertSet('coverageStatus', 'ok')
        ->assertSet('uncoveredTypesCount', 0)
        ->assertSet('fidelity', 'meta');
});

it('exposes coverage warning context in stats', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $tenant->workspace_id,
    ]);

    $snapshot = BaselineSnapshot::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);

    BaselineTenantAssignment::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $compareRun = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => OperationRunType::BaselineCompare->value,
        'status' => OperationRunStatus::Completed->value,
        'outcome' => OperationRunOutcome::PartiallySucceeded->value,
        'completed_at' => now(),
        'context' => [
            'baseline_profile_id' => (int) $profile->getKey(),
            'baseline_snapshot_id' => (int) $snapshot->getKey(),
            'baseline_compare' => [
                'coverage' => [
                    'effective_types' => ['deviceConfiguration', 'deviceCompliancePolicy'],
                    'covered_types' => ['deviceConfiguration'],
                    'uncovered_types' => ['deviceCompliancePolicy'],
                    'proof' => true,
                ],
                'fidelity' => 'meta',
            ],
        ],
    ]);

    baselineCompareLandingLivewire($tenant)
        ->call('refreshStats')
        ->assertSet('operationRunId', (int) $compareRun->getKey())
        ->assertSet('coverageStatus', 'warning')
        ->assertSet('uncoveredTypesCount', 1)
        ->assertSet('uncoveredTypes', ['deviceCompliancePolicy'])
        ->assertSet('fidelity', 'meta');
});

it('shows the latest blocked capture explanation when no consumable baseline exists yet', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []],
    ]);

    BaselineTenantAssignment::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => OperationRunType::BaselineCapture->value,
        'status' => OperationRunStatus::Completed->value,
        'outcome' => OperationRunOutcome::Blocked->value,
        'completed_at' => now(),
        'context' => [
            'baseline_profile_id' => (int) $profile->getKey(),
            'reason_code' => BaselineReasonCodes::CAPTURE_INVENTORY_FAILED,
            'baseline_capture' => [
                'reason_code' => BaselineReasonCodes::CAPTURE_INVENTORY_FAILED,
                'current_baseline_changed' => false,
            ],
        ],
    ]);

    baselineCompareLandingLivewire($tenant)
        ->assertSet('state', 'no_snapshot')
        ->assertSet('snapshotId', null)
        ->assertSet('message', 'The latest inventory sync failed, so this capture could not use a credible upstream basis.');
});

it('keeps compare available against the prior consumable snapshot after a zero-subject capture', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $tenant->workspace_id,
    ]);

    $snapshot = BaselineSnapshot::factory()->complete()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);

    BaselineTenantAssignment::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => OperationRunType::BaselineCapture->value,
        'status' => OperationRunStatus::Completed->value,
        'outcome' => OperationRunOutcome::PartiallySucceeded->value,
        'completed_at' => now(),
        'context' => [
            'baseline_profile_id' => (int) $profile->getKey(),
            'reason_code' => BaselineReasonCodes::CAPTURE_ZERO_SUBJECTS,
            'baseline_capture' => [
                'reason_code' => BaselineReasonCodes::CAPTURE_ZERO_SUBJECTS,
                'current_baseline_changed' => false,
                'previous_current_snapshot_exists' => true,
            ],
        ],
    ]);

    baselineCompareLandingLivewire($tenant)
        ->assertSet('state', 'idle')
        ->assertSet('snapshotId', (int) $snapshot->getKey())
        ->assertActionEnabled('compareNow');
});
