<?php

declare(strict_types=1);

use Tests\Support\TestLaneManifest;

it('declares the six checked-in lanes with a single fast-feedback default and the spec 208 plus 209 metadata surfaces', function (): void {
    $manifest = TestLaneManifest::manifest();
    $laneIds = array_column($manifest['lanes'], 'id');
    $defaultLanes = array_values(array_filter(
        $manifest['lanes'],
        static fn (array $lane): bool => $lane['defaultEntryPoint'] === true,
    ));

    expect($manifest['version'])->toBe(3)
        ->and($manifest['artifactDirectory'])->toBe('storage/logs/test-lanes')
        ->and($manifest['mainlineBranch'])->toBe('dev')
        ->and($manifest)->toHaveKeys([
            'classifications',
            'families',
            'mixedFileResolutions',
            'placementRules',
            'driftGuards',
            'budgetTargets',
            'lanes',
            'workflowProfiles',
            'laneBindings',
            'budgetEnforcementProfiles',
            'artifactPublicationContracts',
            'trendContractVersion',
            'laneTrendPolicies',
            'failureClasses',
            'familyBudgets',
            'heavyGovernanceBudgetContract',
            'heavyGovernanceHotspotInventory',
            'heavyGovernanceDecompositionRecords',
            'heavyGovernanceSlimmingDecisions',
            'heavyGovernanceBudgetSnapshots',
            'heavyGovernanceBudgetOutcome',
            'heavyGovernanceAuthorGuidance',
        ])
        ->and($laneIds)->toEqualCanonicalizing([
            'fast-feedback',
            'confidence',
            'browser',
            'heavy-governance',
            'profiling',
            'junit',
        ])
        ->and($defaultLanes)->toHaveCount(1)
        ->and($defaultLanes[0]['id'])->toBe('fast-feedback');
});

it('publishes the CI workflow matrix, lane bindings, artifact contracts, and failure classes from repo truth', function (): void {
    $workflowProfiles = collect(TestLaneManifest::workflowProfiles())->keyBy('workflowId');
    $laneBindings = collect(TestLaneManifest::laneBindings())->keyBy('laneId');
    $artifactContracts = collect(TestLaneManifest::artifactPublicationContracts())->keyBy('laneId');
    $failureClasses = collect(TestLaneManifest::failureClasses())->keyBy('failureClassId');

    expect($workflowProfiles->keys()->all())->toEqualCanonicalizing([
        'pr-fast-feedback',
        'main-confidence',
        'heavy-governance-manual',
        'heavy-governance-scheduled',
        'browser-manual',
        'browser-scheduled',
    ])
        ->and($workflowProfiles->get('pr-fast-feedback')['laneBindings'])->toBe(['fast-feedback'])
        ->and($workflowProfiles->get('main-confidence')['branchFilters'])->toBe(['dev'])
        ->and($workflowProfiles->get('heavy-governance-scheduled')['scheduleCron'])->toBe('17 4 * * 1-5')
        ->and($workflowProfiles->get('browser-scheduled')['scheduleCron'])->toBe('43 4 * * 1-5')
        ->and($laneBindings->get('fast-feedback')['executionWrapper'])->toBe('scripts/platform-test-lane')
        ->and($laneBindings->get('confidence')['requiredArtifacts'])->toEqualCanonicalizing(['summary.md', 'budget.json', 'report.json', 'junit.xml', 'trend-history.json'])
        ->and($artifactContracts->get('fast-feedback')['retentionClass'])->toBe('pr-short')
        ->and($artifactContracts->get('fast-feedback')['requiredFiles'])->toContain('trend-history.json')
        ->and($artifactContracts->get('browser')['uploadGroupName'])->toBe('browser-artifacts')
        ->and($failureClasses->keys()->all())->toEqualCanonicalizing([
            'test-failure',
            'wrapper-failure',
            'budget-breach',
            'artifact-publication-failure',
            'infrastructure-failure',
        ]);
});

it('keeps every lane declaration populated with governance metadata, selectors, and segmented family expectations', function (): void {
    foreach (TestLaneManifest::manifest()['lanes'] as $lane) {
        expect(trim($lane['description']))->not->toBe('')
            ->and(trim($lane['intendedAudience']))->not->toBe('')
            ->and($lane['includedFamilies'])->not->toBeEmpty()
            ->and($lane['ownershipExpectations'])->not->toBe('')
            ->and($lane['artifacts'])->not->toBeEmpty()
            ->and($lane['budget']['thresholdSeconds'])->toBeGreaterThan(0)
            ->and($lane['budget']['baselineSource'])->toBeString()
            ->and($lane['dbStrategy']['connectionMode'])->toBeString();

        $selectors = $lane['selectors'];

        foreach ([
            'includeSuites',
            'includePaths',
            'includeGroups',
            'includeFiles',
            'excludeSuites',
            'excludePaths',
            'excludeGroups',
            'excludeFiles',
        ] as $selectorKey) {
            expect($selectors)->toHaveKey($selectorKey);
        }
    }

    expect(TestLaneManifest::lane('confidence')['includedFamilies'])->toContain('ui-light', 'ui-workflow')
        ->and(TestLaneManifest::lane('confidence')['excludedFamilies'])->toContain('surface-guard', 'discovery-heavy')
        ->and(TestLaneManifest::lane('heavy-governance')['includedFamilies'])->toContain('surface-guard', 'discovery-heavy')
        ->and(TestLaneManifest::lane('heavy-governance')['scopeBoundaryNote'])->toContain('full-suite repair ownership')
        ->and(TestLaneManifest::lane('browser')['scopeBoundaryNote'])->toContain('classification-only');
});

it('exposes the spec 208 classification catalog and seeded family inventory with required metadata', function (): void {
    $classifications = collect(TestLaneManifest::classifications())->keyBy('classificationId');
    $families = collect(TestLaneManifest::families())->keyBy('familyId');

    expect($classifications->keys()->all())->toEqualCanonicalizing([
        'ui-light',
        'ui-workflow',
        'surface-guard',
        'discovery-heavy',
        'browser',
    ])
        ->and($classifications->get('browser')['allowedLaneIds'])->toBe(['browser'])
        ->and($classifications->get('surface-guard')['defaultLaneId'])->toBe('heavy-governance')
        ->and($classifications->get('discovery-heavy')['forbiddenLaneIds'])->toContain('fast-feedback', 'confidence')
        ->and($families->has('baseline-profile-start-surfaces'))->toBeTrue()
        ->and($families->has('findings-workflow-surfaces'))->toBeTrue()
        ->and($families->has('finding-bulk-actions-workflow'))->toBeTrue()
        ->and($families->has('drift-bulk-triage-all-matching'))->toBeTrue()
        ->and($families->has('policy-resource-admin-search-parity'))->toBeTrue()
        ->and($families->has('workspace-only-admin-surface-independence'))->toBeTrue()
        ->and($families->has('workspace-settings-slice-management'))->toBeTrue()
        ->and($families->has('provider-dispatch-gate-coverage'))->toBeTrue()
        ->and($families->has('baseline-compare-matrix-workflow'))->toBeTrue()
        ->and($families->has('browser-smoke'))->toBeTrue()
        ->and($families->has('no-legacy-guardrail'))->toBeTrue()
        ->and($families->get('browser-smoke')['hotspotFiles'])->toContain(
            'tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php',
            'tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php',
        )
        ->and($families->get('no-legacy-guardrail')['targetLaneId'])->toBe('heavy-governance');

    foreach (TestLaneManifest::families() as $family) {
        expect(trim($family['purpose']))->not->toBe('')
            ->and(trim($family['currentLaneId']))->not->toBe('')
            ->and(trim($family['targetLaneId']))->not->toBe('')
            ->and($family['selectors'])->not->toBeEmpty()
            ->and($family['hotspotFiles'])->not->toBeEmpty()
            ->and(trim($family['validationStatus']))->not->toBe('');

        if ($family['targetLaneId'] === 'confidence') {
            expect(trim((string) ($family['confidenceRationale'] ?? '')))->not->toBe('');
        }
    }
});

it('keeps family budgets derived from the generic budget targets for report consumers', function (): void {
    $familyBudgets = TestLaneManifest::familyBudgets();

    expect($familyBudgets)->not->toBeEmpty()
        ->and($familyBudgets[0])->toHaveKeys(['familyId', 'targetType', 'targetId', 'selectors', 'thresholdSeconds'])
        ->and(collect($familyBudgets)->pluck('familyId')->all())
        ->toContain('action-surface-contract', 'browser-smoke', 'baseline-compare-matrix-workflow', 'baseline-profile-start-surfaces', 'drift-bulk-triage-all-matching', 'finding-bulk-actions-workflow', 'findings-workflow-surfaces', 'provider-dispatch-gate-coverage', 'workspace-only-admin-surface-independence', 'workspace-settings-slice-management');
});

it('publishes the heavy-governance contract, inventory, and guidance surfaces needed for honest rerun review', function (): void {
    $contract = TestLaneManifest::heavyGovernanceBudgetContract();
    $inventory = collect(TestLaneManifest::heavyGovernanceHotspotInventory());

    expect($contract['summaryThresholdSeconds'])->toBe(300.0)
        ->and($contract['evaluationThresholdSeconds'])->toBe(200.0)
        ->and($contract['normalizedThresholdSeconds'])->toBeGreaterThanOrEqual(300.0)
        ->and($contract['decisionStatus'])->toBeIn(['recovered', 'recalibrated'])
        ->and($inventory)->toHaveCount(6)
        ->and($inventory->pluck('familyId')->all())->toEqual([
            'baseline-profile-start-surfaces',
            'action-surface-contract',
            'ops-ux-governance',
            'findings-workflow-surfaces',
            'finding-bulk-actions-workflow',
            'workspace-settings-slice-management',
        ])
        ->and(collect(TestLaneManifest::heavyGovernanceBudgetSnapshots()))->toHaveCount(2)
        ->and(TestLaneManifest::heavyGovernanceBudgetOutcome())->toHaveKeys([
            'decisionStatus',
            'finalThresholdSeconds',
            'deltaSeconds',
            'remainingOpenFamilies',
            'followUpDebt',
        ])
        ->and(collect(TestLaneManifest::heavyGovernanceAuthorGuidance())->pluck('ruleId')->all())
        ->toEqualCanonicalizing([
            'heavy-family-reuse-before-creation',
            'heavy-family-create-only-for-new-trust',
            'split-discovery-workflow-surface-concerns',
            'retain-intentional-heavy-depth-explicitly',
            'record-helper-or-fixture-residuals',
        ]);
});
