<?php

declare(strict_types=1);

use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Support\OpsUx\GovernanceRunDiagnosticSummaryBuilder;
use App\Support\OpsUx\SummaryCountsNormalizer;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Feature\Concerns\BuildsGovernanceArtifactTruthFixtures;

uses(RefreshDatabase::class, BuildsGovernanceArtifactTruthFixtures::class);

it('derives a blocked baseline capture summary with prerequisite-focused next steps', function (): void {
    $tenant = ManagedEnvironment::factory()->create();

    $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' => 'missing_capability',
            'baseline_capture' => [
                'subjects_total' => 0,
                'gaps' => [
                    'count' => 0,
                ],
            ],
        ],
        'failure_summary' => [[
            'reason_code' => 'missing_capability',
            'message' => 'A required capability is missing for this run.',
        ]],
        'completed_at' => now(),
    ]);

    $summary = app(GovernanceRunDiagnosticSummaryBuilder::class)->build($run);

    expect($summary)->not->toBeNull()
        ->and($summary?->headline)->toContain('No baseline was captured')
        ->and($summary?->dominantCause['label'])->toBe('No governed subjects captured')
        ->and($summary?->nextActionCategory)->toBe('refresh_prerequisite_data')
        ->and($summary?->affectedScaleCue['label'])->toBe('Capture scope')
        ->and($summary?->affectedScaleCue['value'])->toContain('0 governed subjects');
});

it('derives an ambiguous baseline compare summary with affected scale and scope review guidance', function (): void {
    $tenant = ManagedEnvironment::factory()->create();

    $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' => 'ambiguous_subjects',
                'subjects_total' => 12,
                'evidence_gaps' => [
                    'count' => 4,
                ],
                'coverage' => [
                    'proof' => false,
                ],
            ],
        ],
        'summary_counts' => [
            'total' => 0,
            'processed' => 0,
            'errors_recorded' => 2,
        ],
        'completed_at' => now(),
    ]);

    $summary = app(GovernanceRunDiagnosticSummaryBuilder::class)->build($run);

    expect($summary)->not->toBeNull()
        ->and($summary?->headline)->toContain('ambiguous subject matching')
        ->and($summary?->dominantCause['label'])->toBe('Ambiguous matches')
        ->and($summary?->nextActionCategory)->toBe('review_scope_or_ambiguous_matches')
        ->and($summary?->affectedScaleCue['label'])->toBe('Affected subjects')
        ->and($summary?->affectedScaleCue['value'])->toContain('4 governed subjects');
});

it('keeps execution outcome separate from artifact impact for stale evidence snapshot runs', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

    $run = $this->makeArtifactTruthRun($tenant, 'tenant.evidence.snapshot.generate');

    $this->makeStaleArtifactTruthEvidenceSnapshot(
        tenant: $tenant,
        snapshotOverrides: [
            'operation_run_id' => (int) $run->getKey(),
        ],
    );

    $summary = app(GovernanceRunDiagnosticSummaryBuilder::class)->build($run->fresh());

    expect($summary)->not->toBeNull()
        ->and($summary?->executionOutcomeLabel)->toBe('Completed successfully')
        ->and($summary?->artifactImpactLabel)->not->toBe($summary?->executionOutcomeLabel)
        ->and($summary?->headline)->toContain('stale')
        ->and($summary?->nextActionCategory)->toBe('refresh_prerequisite_data');
});

it('derives resume capture or generation when a compare run records a resume token', function (): void {
    $tenant = ManagedEnvironment::factory()->create();

    $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' => [
                'resume_token' => 'resume-token-220',
                'evidence_gaps' => [
                    'count' => 2,
                ],
            ],
        ],
        'completed_at' => now(),
    ]);

    $summary = app(GovernanceRunDiagnosticSummaryBuilder::class)->build($run);

    expect($summary)->not->toBeNull()
        ->and($summary?->nextActionCategory)->toBe('resume_capture_or_generation')
        ->and($summary?->headline)->toContain('evidence capture still needs to resume');
});

it('keeps deterministic multi-cause ordering for degraded review composition runs', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

    $run = $this->makeArtifactTruthRun($tenant, 'environment.review.compose');
    $snapshot = $this->makeStaleArtifactTruthEvidenceSnapshot($tenant, [
        'operation_run_id' => null,
    ]);

    $this->makeArtifactTruthReview(
        tenant: $tenant,
        user: $user,
        snapshot: $snapshot,
        reviewOverrides: [
            'operation_run_id' => (int) $run->getKey(),
            'completeness_state' => 'partial',
        ],
        summaryOverrides: [
            'section_state_counts' => [
                'complete' => 4,
                'partial' => 1,
                'missing' => 1,
                'stale' => 0,
            ],
        ],
    );

    $builder = app(GovernanceRunDiagnosticSummaryBuilder::class);
    $first = $builder->build($run->fresh());
    $second = $builder->build($run->fresh());

    expect($first)->not->toBeNull()
        ->and($second)->not->toBeNull()
        ->and($first?->dominantCause['label'])->toBe('Missing sections')
        ->and($first?->secondaryCauses[0]['label'] ?? null)->toBe('Stale evidence basis')
        ->and($first?->secondaryCauses)->toEqual($second?->secondaryCauses)
        ->and($first?->headline)->toContain('missing sections and stale evidence');
});

it('derives no further action for publishable review pack runs', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

    $run = $this->makeArtifactTruthRun($tenant, 'environment.review_pack.generate');
    $snapshot = $this->makeArtifactTruthEvidenceSnapshot($tenant, [
        'operation_run_id' => null,
    ]);
    $review = $this->makeArtifactTruthReview($tenant, $user, $snapshot, [
        'operation_run_id' => null,
    ]);

    $this->makeArtifactTruthReviewPack(
        tenant: $tenant,
        user: $user,
        snapshot: $snapshot,
        review: $review,
        packOverrides: [
            'operation_run_id' => (int) $run->getKey(),
        ],
    );

    $summary = app(GovernanceRunDiagnosticSummaryBuilder::class)->build($run->fresh());

    expect($summary)->not->toBeNull()
        ->and($summary?->nextActionCategory)->toBe('no_further_action')
        ->and($summary?->nextActionText)->toBe('No action needed.');
});

it('does not invent new summary count keys while deriving scale cues', function (): void {
    $tenant = ManagedEnvironment::factory()->create();

    $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' => 7,
            'custom_noise' => 99,
        ],
        'context' => [
            'baseline_compare' => [],
        ],
        'completed_at' => now(),
    ]);

    $summary = app(GovernanceRunDiagnosticSummaryBuilder::class)->build($run);

    expect($summary)->not->toBeNull()
        ->and(array_keys(SummaryCountsNormalizer::normalize(is_array($run->summary_counts) ? $run->summary_counts : [])))
        ->toBe(['total'])
        ->and($summary?->affectedScaleCue['source'])->toBe('summary_counts')
        ->and($summary?->affectedScaleCue['label'])->toBe('Total');
});
