<?php

declare(strict_types=1);

use App\Filament\Pages\Operations\TenantlessOperationRunViewer;
use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\BaselineTenantAssignment;
use App\Models\OperationRun;
use App\Services\Baselines\BaselineCaptureService;
use App\Services\Baselines\BaselineCompareService;
use App\Support\Baselines\BaselineCompareReasonCode;
use App\Support\Baselines\BaselineCompareStats;
use App\Support\Baselines\BaselineReasonCodes;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Queue;
use Livewire\Features\SupportTesting\Testable;
use Livewire\Livewire;

function visibleLivewireText(Testable $component): string
{
    $html = $component->html();
    $html = preg_replace('/<script\b[^>]*>.*?<\/script>/is', '', $html) ?? $html;
    $html = preg_replace('/<style\b[^>]*>.*?<\/style>/is', '', $html) ?? $html;
    $html = preg_replace('/\s+wire:snapshot="[^"]*"/', '', $html) ?? $html;
    $html = preg_replace('/\s+wire:effects="[^"]*"/', '', $html) ?? $html;

    return trim((string) preg_replace('/\s+/', ' ', strip_tags($html)));
}

it('shows run outcome and baseline artifact truth as separate facts on the run detail page', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

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

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

    $run = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => 'baseline_capture',
        'status' => 'completed',
        'outcome' => 'failed',
        'context' => [
            'baseline_profile_id' => (int) $profile->getKey(),
            'baseline_snapshot_id' => (int) $snapshot->getKey(),
            'result' => [
                'snapshot_id' => (int) $snapshot->getKey(),
                'snapshot_lifecycle' => 'incomplete',
            ],
            'reason_code' => BaselineReasonCodes::SNAPSHOT_CAPTURE_FAILED,
        ],
        'failure_summary' => [
            ['reason_code' => BaselineReasonCodes::SNAPSHOT_CAPTURE_FAILED, 'message' => 'Snapshot capture stopped after persistence failed.'],
        ],
        'completed_at' => now(),
    ]);

    $truth = app(ArtifactTruthPresenter::class)->forOperationRun($run->fresh());
    $explanation = $truth->operatorExplanation;

    Filament::setTenant(null, true);
    $this->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);
    session([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);

    $component = Livewire::actingAs($user)
        ->test(TenantlessOperationRunViewer::class, ['run' => $run])
        ->assertSee('Decision')
        ->assertSee('Outcome')
        ->assertSee('Artifact truth')
        ->assertSee('Execution failed')
        ->assertSee('The baseline capture finished without a usable snapshot.')
        ->assertSee($explanation?->evaluationResultLabel() ?? '')
        ->assertSee($explanation?->trustworthinessLabel() ?? '')
        ->assertSee('Artifact not usable')
        ->assertSee('Primary next step')
        ->assertSee('Artifact truth details')
        ->assertSee('Inspect the related capture diagnostics before using this snapshot')
        ->assertDontSee('Artifact next step');

    $pageText = visibleLivewireText($component);

    expect(mb_substr_count($pageText, 'Primary next step'))->toBe(1)
        ->and(mb_substr_count($pageText, 'Inspect the related capture diagnostics before using this snapshot'))->toBe(1)
        ->and(mb_strpos($pageText, 'Decision'))->toBeLessThan(mb_strpos($pageText, 'Artifact truth details'));
});

it('shows the shared blocked-inventory explanation for baseline capture runs without a usable snapshot', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $run = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => 'baseline_capture',
        'status' => 'completed',
        'outcome' => 'blocked',
        'context' => [
            'reason_code' => BaselineReasonCodes::CAPTURE_INVENTORY_FAILED,
            'baseline_capture' => [
                'reason_code' => BaselineReasonCodes::CAPTURE_INVENTORY_FAILED,
                'current_baseline_changed' => false,
            ],
        ],
        'failure_summary' => [
            ['reason_code' => BaselineReasonCodes::CAPTURE_INVENTORY_FAILED, 'message' => 'Capture blocked because the latest inventory sync failed.'],
        ],
        'completed_at' => now(),
    ]);

    $truth = app(ArtifactTruthPresenter::class)->forOperationRun($run->fresh());
    $explanation = $truth->operatorExplanation;

    Filament::setTenant(null, true);
    $this->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);
    session([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);

    Livewire::actingAs($user)
        ->test(TenantlessOperationRunViewer::class, ['run' => $run])
        ->assertSee('Blocked by prerequisite')
        ->assertSee($explanation?->evaluationResultLabel() ?? '')
        ->assertSee($explanation?->trustworthinessLabel() ?? '')
        ->assertSee('Latest inventory sync failed')
        ->assertSee($explanation?->nextActionText ?? '');
});

it('shows operator explanation facts for baseline compare runs with nested compare reason context', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $run = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => 'baseline_compare',
        'status' => 'completed',
        'outcome' => 'partially_succeeded',
        'context' => [
            'baseline_compare' => [
                'reason_code' => 'evidence_capture_incomplete',
                'coverage' => [
                    'proof' => false,
                ],
                'evidence_gaps' => [
                    'count' => 4,
                ],
            ],
        ],
        'summary_counts' => [
            'total' => 0,
            'processed' => 0,
            'errors_recorded' => 0,
        ],
        'completed_at' => now(),
    ]);

    $truth = app(ArtifactTruthPresenter::class)->forOperationRun($run->fresh());
    $explanation = $truth->operatorExplanation;

    Filament::setTenant(null, true);
    $this->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);
    session([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);

    $component = Livewire::actingAs($user)
        ->test(TenantlessOperationRunViewer::class, ['run' => $run])
        ->assertSee('Decision')
        ->assertSee('Artifact truth')
        ->assertSee('Result meaning')
        ->assertSee('Result trust')
        ->assertSee('Primary next step')
        ->assertSee('Artifact truth details')
        ->assertSee('The compare finished, but no decision-grade result is available yet.')
        ->assertSee($explanation?->evaluationResultLabel() ?? '')
        ->assertSee($explanation?->trustworthinessLabel() ?? '')
        ->assertSee($explanation?->nextActionText ?? '')
        ->assertSee('Resume or rerun evidence capture before relying on this compare result.')
        ->assertDontSee('Artifact next step');

    $pageText = visibleLivewireText($component);

    expect(mb_substr_count($pageText, 'Primary next step'))->toBe(1)
        ->and(mb_substr_count($pageText, 'Resume or rerun evidence capture before relying on this compare result.'))->toBe(1)
        ->and(mb_strpos($pageText, 'Decision'))->toBeLessThan(mb_strpos($pageText, 'Artifact truth details'));
});

it('shows strategy diagnostics and operator-safe failure meaning for strategy-owned compare failures', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $run = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => 'baseline_compare',
        'status' => 'completed',
        'outcome' => 'partially_succeeded',
        'context' => [
            'baseline_compare' => [
                'reason_code' => BaselineCompareReasonCode::StrategyFailed->value,
                'coverage' => [
                    'proof' => true,
                    'effective_types' => ['conditionalAccessPolicy'],
                    'covered_types' => ['conditionalAccessPolicy'],
                    'uncovered_types' => [],
                ],
                'evidence_gaps' => [
                    'count' => 1,
                    'by_reason' => [
                        'strategy_failed' => 1,
                    ],
                ],
                'strategy' => [
                    'key' => 'intune_policy',
                    'selection_state' => 'supported',
                    'operator_reason' => 'Compare strategy resolved successfully.',
                    'execution_diagnostics' => [
                        'failed' => true,
                        'exception_class' => RuntimeException::class,
                    ],
                    'state_counts' => [
                        'failed' => 2,
                    ],
                ],
            ],
        ],
        'summary_counts' => [
            'total' => 0,
            'processed' => 0,
            'errors_recorded' => 1,
        ],
        'completed_at' => now(),
    ]);

    $truth = app(ArtifactTruthPresenter::class)->forOperationRun($run->fresh());
    $explanation = $truth->operatorExplanation;

    Filament::setTenant(null, true);
    $this->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);
    session([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);

    Livewire::actingAs($user)
        ->test(TenantlessOperationRunViewer::class, ['run' => $run])
        ->assertSee('The compare finished, but a compare strategy failure kept the result incomplete.')
        ->assertSee($explanation?->nextActionText ?? '')
        ->assertSee('Compare strategy')
        ->assertSee('Intune Policy')
        ->assertSee('Strategy selection')
        ->assertSee('Supported')
        ->assertSee('Strategy subject states')
        ->assertSee('Failed 2')
        ->assertSee('Baseline compare evidence')
        ->assertSee('RuntimeException');
});

it('deduplicates repeated artifact truth explanation text for follow-up runs without a usable artifact', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $run = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => 'baseline_compare',
        'status' => 'completed',
        'outcome' => 'partially_succeeded',
        'summary_counts' => [
            'total' => 50,
            'processed' => 47,
            'failed' => 3,
        ],
        'context' => [],
        'completed_at' => now(),
    ]);

    Filament::setTenant(null, true);
    $this->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);
    session([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);

    $component = Livewire::actingAs($user)
        ->test(TenantlessOperationRunViewer::class, ['run' => $run])
        ->assertSee('Decision')
        ->assertSee('Artifact truth details')
        ->assertSee('The operation finished without a usable artifact result.');

    $pageText = visibleLivewireText($component);

    expect(mb_substr_count($pageText, 'The operation finished without a usable artifact result.'))->toBe(1)
        ->and(mb_strpos($pageText, 'Decision'))->toBeLessThan(mb_strpos($pageText, 'Artifact truth details'));
});

it('keeps the compact tenant summary at least as cautious as the canonical run detail for suppressed compare results', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $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(),
    ]);

    $run = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => 'baseline_compare',
        'status' => 'completed',
        'outcome' => 'partially_succeeded',
        'context' => [
            'baseline_profile_id' => (int) $profile->getKey(),
            'baseline_snapshot_id' => (int) $snapshot->getKey(),
            'baseline_compare' => [
                'reason_code' => BaselineCompareReasonCode::CoverageUnproven->value,
                'coverage' => [
                    'effective_types' => ['deviceConfiguration', 'deviceCompliancePolicy'],
                    'covered_types' => ['deviceConfiguration'],
                    'uncovered_types' => ['deviceCompliancePolicy'],
                    'proof' => false,
                ],
            ],
        ],
        'summary_counts' => [
            'total' => 0,
            'processed' => 0,
            'errors_recorded' => 2,
        ],
        'completed_at' => now(),
    ]);

    $summary = BaselineCompareStats::forTenant($tenant)->summaryAssessment();
    $truth = app(ArtifactTruthPresenter::class)->forOperationRun($run->fresh());
    $explanation = $truth->operatorExplanation;

    expect($summary->stateFamily)->not->toBe('positive')
        ->and($summary->evaluationResult)->toBe('suppressed_result')
        ->and($summary->headline)->toBe('The last compare finished, but normal result output was suppressed.')
        ->and($explanation?->evaluationResult)->toBe('suppressed_result');

    Filament::setTenant(null, true);
    $this->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);
    session([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);

    Livewire::actingAs($user)
        ->test(TenantlessOperationRunViewer::class, ['run' => $run])
        ->assertSee('The compare finished, but no decision-grade result is available yet.')
        ->assertSee($explanation?->evaluationResultLabel() ?? '')
        ->assertSee($explanation?->trustworthinessLabel() ?? '')
        ->assertDontSee('No confirmed drift in the latest baseline compare.');
});

it('records canonical effective scope and compatibility projection for baseline capture runs', function (): void {
    Queue::fake();

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

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

    $result = app(BaselineCaptureService::class)->startCapture($profile, $tenant, $user);

    expect($result['ok'])->toBeTrue();

    $run = $result['run'];
    $effectiveScope = is_array(data_get($run->context, 'effective_scope')) ? data_get($run->context, 'effective_scope') : [];

    expect(data_get($effectiveScope, 'canonical_scope.version'))->toBe(2)
        ->and(data_get($effectiveScope, 'canonical_scope.entries.0.domain_key'))->toBe('intune')
        ->and(data_get($effectiveScope, 'canonical_scope.entries.0.subject_class'))->toBe('policy')
        ->and(data_get($effectiveScope, 'canonical_scope.entries.0.subject_type_keys'))->toBe(['deviceConfiguration'])
        ->and(data_get($effectiveScope, 'legacy_projection.policy_types'))->toBe(['deviceConfiguration'])
        ->and(data_get($effectiveScope, 'legacy_projection.foundation_types'))->toBe([])
        ->and(data_get($effectiveScope, 'selected_type_keys'))->toBe(['deviceConfiguration'])
        ->and(data_get($effectiveScope, 'allowed_type_keys'))->toBe(['deviceConfiguration'])
        ->and(data_get($effectiveScope, 'unsupported_type_keys'))->toBe([])
        ->and(data_get($run->context, 'baseline_capture.inventory_sync_run_id'))->toBe((int) $inventorySyncRun->getKey())
        ->and(data_get($run->context, 'baseline_capture.eligibility.phase'))->toBe('preflight')
        ->and(data_get($run->context, 'baseline_capture.eligibility.ok'))->toBeTrue();
});

it('normalizes legacy compare assignment overrides into canonical effective scope without rewriting the override row', function (): void {
    Queue::fake();

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

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

    $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()]);

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

    DB::table('baseline_tenant_assignments')
        ->where('id', (int) $assignment->getKey())
        ->update([
            'override_scope_jsonb' => json_encode([
                'policy_types' => ['deviceConfiguration'],
                'foundation_types' => [],
            ], JSON_THROW_ON_ERROR),
            'updated_at' => now(),
        ]);

    $result = app(BaselineCompareService::class)->startCompare($tenant, $user);

    expect($result['ok'])->toBeTrue();

    $run = $result['run'];
    $effectiveScope = is_array(data_get($run->context, 'effective_scope')) ? data_get($run->context, 'effective_scope') : [];
    $rawOverride = DB::table('baseline_tenant_assignments')
        ->where('id', (int) $assignment->getKey())
        ->value('override_scope_jsonb');

    expect(data_get($effectiveScope, 'canonical_scope.version'))->toBe(2)
        ->and(data_get($effectiveScope, 'canonical_scope.entries.0.subject_type_keys'))->toBe(['deviceConfiguration'])
        ->and(data_get($effectiveScope, 'legacy_projection.policy_types'))->toBe(['deviceConfiguration'])
        ->and(data_get($effectiveScope, 'selected_type_keys'))->toBe(['deviceConfiguration'])
        ->and(json_decode((string) $rawOverride, true, flags: JSON_THROW_ON_ERROR))->toBe([
            'policy_types' => ['deviceConfiguration'],
            'foundation_types' => [],
        ]);
});
