<?php

declare(strict_types=1);

use App\Jobs\CaptureBaselineSnapshotJob;
use App\Jobs\CompareBaselineToTenantJob;
use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\BaselineSnapshotItem;
use App\Models\InventoryItem;
use App\Models\OperationRun;
use App\Services\Baselines\BaselineSnapshotIdentity;
use App\Services\Baselines\InventoryMetaContract;
use App\Services\Intune\AuditLogger;
use App\Services\OperationRunService;
use App\Support\Baselines\BaselineCaptureMode;
use App\Support\Baselines\BaselineSubjectKey;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;

it('persists the same structured gap subjects for unchanged capture and compare inputs', function (): void {
    config()->set('tenantpilot.baselines.full_content_capture.enabled', true);
    config()->set('tenantpilot.baselines.full_content_capture.max_items_per_run', 50);
    config()->set('tenantpilot.baselines.full_content_capture.max_concurrency', 1);
    config()->set('tenantpilot.baselines.full_content_capture.max_retries', 0);

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

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

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

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

    $policySubjectKey = BaselineSubjectKey::fromDisplayName('Deterministic Missing Policy');
    $foundationSubjectKey = BaselineSubjectKey::fromDisplayName('Deterministic Foundation');

    BaselineSnapshotItem::factory()->create([
        'baseline_snapshot_id' => (int) $snapshot->getKey(),
        'subject_type' => 'policy',
        'subject_external_id' => BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', (string) $policySubjectKey),
        'subject_key' => (string) $policySubjectKey,
        'policy_type' => 'deviceConfiguration',
        'baseline_hash' => hash('sha256', 'deterministic-policy'),
        'meta_jsonb' => ['display_name' => 'Deterministic Missing Policy'],
    ]);

    BaselineSnapshotItem::factory()->create([
        'baseline_snapshot_id' => (int) $snapshot->getKey(),
        'subject_type' => 'policy',
        'subject_external_id' => BaselineSubjectKey::workspaceSafeSubjectExternalId('roleScopeTag', (string) $foundationSubjectKey),
        'subject_key' => (string) $foundationSubjectKey,
        'policy_type' => 'roleScopeTag',
        'baseline_hash' => hash('sha256', 'deterministic-foundation'),
        'meta_jsonb' => ['display_name' => 'Deterministic Foundation'],
    ]);

    $inventorySyncRun = createInventorySyncOperationRunWithCoverage(
        tenant: $tenant,
        statusByType: [
            'deviceConfiguration' => 'succeeded',
            'roleScopeTag' => 'succeeded',
        ],
        foundationTypes: ['roleScopeTag'],
    );

    InventoryItem::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'external_id' => 'deterministic-policy',
        'policy_type' => 'deviceConfiguration',
        'display_name' => 'Deterministic Missing Policy',
        'meta_jsonb' => ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'etag-deterministic-policy'],
        'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(),
        'last_seen_at' => now(),
    ]);

    InventoryItem::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'external_id' => 'deterministic-foundation',
        'policy_type' => 'roleScopeTag',
        'display_name' => 'Deterministic Foundation',
        'meta_jsonb' => ['odata_type' => '#microsoft.graph.roleScopeTag', 'etag' => 'etag-deterministic-foundation'],
        'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(),
        'last_seen_at' => now(),
    ]);

    $captureRunA = OperationRun::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'type' => OperationRunType::BaselineCapture->value,
        'status' => OperationRunStatus::Queued->value,
        'outcome' => OperationRunOutcome::Pending->value,
        'context' => [
            'baseline_profile_id' => (int) $profile->getKey(),
            'source_environment_id' => (int) $tenant->getKey(),
            'effective_scope' => [
                'policy_types' => ['deviceConfiguration'],
                'foundation_types' => ['roleScopeTag'],
                'truthful_types' => ['deviceConfiguration', 'roleScopeTag'],
            ],
            'capture_mode' => BaselineCaptureMode::FullContent->value,
        ],
        'user_id' => (int) $user->getKey(),
    ]);

    $captureRunB = OperationRun::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'type' => OperationRunType::BaselineCapture->value,
        'status' => OperationRunStatus::Queued->value,
        'outcome' => OperationRunOutcome::Pending->value,
        'context' => $captureRunA->context,
        'user_id' => (int) $user->getKey(),
    ]);

    (new CaptureBaselineSnapshotJob($captureRunA))->handle(
        app(BaselineSnapshotIdentity::class),
        app(InventoryMetaContract::class),
        app(AuditLogger::class),
        app(OperationRunService::class),
    );

    (new CaptureBaselineSnapshotJob($captureRunB))->handle(
        app(BaselineSnapshotIdentity::class),
        app(InventoryMetaContract::class),
        app(AuditLogger::class),
        app(OperationRunService::class),
    );

    $compareRunA = OperationRun::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'type' => OperationRunType::BaselineCompare->value,
        'status' => OperationRunStatus::Queued->value,
        'outcome' => OperationRunOutcome::Pending->value,
        'context' => [
            'baseline_profile_id' => (int) $profile->getKey(),
            'baseline_snapshot_id' => (int) $snapshot->getKey(),
            'effective_scope' => [
                'policy_types' => ['deviceConfiguration'],
                'foundation_types' => ['roleScopeTag'],
                'truthful_types' => ['deviceConfiguration', 'roleScopeTag'],
            ],
            'capture_mode' => BaselineCaptureMode::FullContent->value,
        ],
        'user_id' => (int) $user->getKey(),
    ]);

    $compareRunB = OperationRun::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'type' => OperationRunType::BaselineCompare->value,
        'status' => OperationRunStatus::Queued->value,
        'outcome' => OperationRunOutcome::Pending->value,
        'context' => $compareRunA->context,
        'user_id' => (int) $user->getKey(),
    ]);

    (new CompareBaselineToTenantJob($compareRunA))->handle(
        app(BaselineSnapshotIdentity::class),
        app(AuditLogger::class),
        app(OperationRunService::class),
    );

    (new CompareBaselineToTenantJob($compareRunB))->handle(
        app(BaselineSnapshotIdentity::class),
        app(AuditLogger::class),
        app(OperationRunService::class),
    );

    $captureSubjectsA = collect(data_get($captureRunA->fresh()->context, 'baseline_capture.gaps.subjects', []))->sortBy('policy_type')->values()->all();
    $captureSubjectsB = collect(data_get($captureRunB->fresh()->context, 'baseline_capture.gaps.subjects', []))->sortBy('policy_type')->values()->all();
    $compareSubjectsA = collect(data_get($compareRunA->fresh()->context, 'baseline_compare.evidence_gaps.subjects', []))->sortBy('policy_type')->values()->all();
    $compareSubjectsB = collect(data_get($compareRunB->fresh()->context, 'baseline_compare.evidence_gaps.subjects', []))->sortBy('policy_type')->values()->all();

    expect($captureSubjectsA)->toBe($captureSubjectsB)
        ->and($compareSubjectsA)->toBe($compareSubjectsB);
});
