<?php

use App\Jobs\CompareBaselineToTenantJob;
use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\BaselineSnapshotItem;
use App\Models\Finding;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\WorkspaceSetting;
use App\Services\Baselines\BaselineAutoCloseService;
use App\Services\Baselines\BaselineSnapshotIdentity;
use App\Services\Intune\AuditLogger;
use App\Services\OperationRunService;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;

/**
 * @return array{0: User, 1: ManagedEnvironment, 2: BaselineProfile, 3: BaselineSnapshot}
 */
function createBaselineOperabilityFixture(): array
{
    [$user, $tenant] = createUserWithTenant(role: 'owner');

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

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

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

    return [$user, $tenant, $profile, $snapshot];
}

function runBaselineCompareForSnapshot(
    User $user,
    ManagedEnvironment $tenant,
    BaselineProfile $profile,
    BaselineSnapshot $snapshot,
): OperationRun {
    createInventorySyncOperationRunWithCoverage(
        tenant: $tenant,
        statusByType: ['deviceConfiguration' => 'succeeded'],
    );

    $operationRuns = app(OperationRunService::class);

    $run = $operationRuns->ensureRunWithIdentity(
        tenant: $tenant,
        type: OperationRunType::BaselineCompare->value,
        identityInputs: ['baseline_profile_id' => (int) $profile->getKey()],
        context: [
            'baseline_profile_id' => (int) $profile->getKey(),
            'baseline_snapshot_id' => (int) $snapshot->getKey(),
            'effective_scope' => ['policy_types' => ['deviceConfiguration']],
        ],
        initiator: $user,
    );

    $job = new CompareBaselineToTenantJob($run);
    $job->handle(
        app(BaselineSnapshotIdentity::class),
        app(AuditLogger::class),
        $operationRuns,
    );

    return $run->fresh();
}

it('resolves stale baseline findings after a fully successful compare', function (): void {
    [$user, $tenant, $profile, $firstSnapshot] = createBaselineOperabilityFixture();

    BaselineSnapshotItem::factory()->create([
        'baseline_snapshot_id' => $firstSnapshot->getKey(),
        'subject_type' => 'policy',
        'subject_external_id' => 'stale-policy',
        'policy_type' => 'deviceConfiguration',
        'baseline_hash' => hash('sha256', 'baseline-content'),
        'meta_jsonb' => ['display_name' => 'Stale Policy'],
    ]);

    $firstRun = runBaselineCompareForSnapshot($user, $tenant, $profile, $firstSnapshot);

    $finding = Finding::query()
        ->where('managed_environment_id', (int) $tenant->getKey())
        ->where('source', 'baseline.compare')
        ->first();

    expect($finding)->not->toBeNull();
    expect($finding?->status)->toBe(Finding::STATUS_NEW);

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

    $profile->update(['active_snapshot_id' => $secondSnapshot->getKey()]);
    $firstRun->update(['completed_at' => now()->subMinute()]);

    $secondRun = runBaselineCompareForSnapshot($user, $tenant, $profile, $secondSnapshot);

    $finding->refresh();
    expect($finding->status)->toBe(Finding::STATUS_RESOLVED);
    expect($finding->resolved_reason)->toBe('no_longer_drifting');
    expect($finding->resolved_at)->not->toBeNull();
    expect($finding->current_operation_run_id)->toBe((int) $secondRun->getKey());
});

dataset('baseline auto close safety gates', [
    'safe and enabled' => [
        OperationRunOutcome::Succeeded->value,
        ['total' => 2, 'processed' => 2, 'failed' => 0],
        null,
        true,
    ],
    'disabled by workspace setting' => [
        OperationRunOutcome::Succeeded->value,
        ['total' => 2, 'processed' => 2, 'failed' => 0],
        false,
        false,
    ],
    'partially succeeded outcome' => [
        OperationRunOutcome::PartiallySucceeded->value,
        ['total' => 2, 'processed' => 2, 'failed' => 0],
        null,
        false,
    ],
    'failed outcome' => [
        OperationRunOutcome::Failed->value,
        ['total' => 2, 'processed' => 2, 'failed' => 0],
        null,
        false,
    ],
    'incomplete processed count' => [
        OperationRunOutcome::Succeeded->value,
        ['total' => 2, 'processed' => 1, 'failed' => 0],
        null,
        false,
    ],
    'failed work recorded' => [
        OperationRunOutcome::Succeeded->value,
        ['total' => 2, 'processed' => 2, 'failed' => 1],
        null,
        false,
    ],
    'missing counters' => [
        OperationRunOutcome::Succeeded->value,
        ['processed' => 2, 'failed' => 0],
        null,
        false,
    ],
]);

it('gates auto close on outcome completion counts and workspace setting', function (
    string $outcome,
    array $summaryCounts,
    ?bool $settingValue,
    bool $expected,
): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    if ($settingValue !== null) {
        WorkspaceSetting::query()->create([
            'workspace_id' => (int) $tenant->workspace_id,
            'domain' => 'baseline',
            'key' => 'auto_close_enabled',
            'value' => $settingValue,
            'updated_by_user_id' => (int) $user->getKey(),
        ]);
    }

    $run = OperationRun::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'user_id' => (int) $user->getKey(),
        'type' => OperationRunType::BaselineCompare->value,
        'status' => OperationRunStatus::Completed->value,
        'outcome' => $outcome,
        'summary_counts' => $summaryCounts,
        'completed_at' => now(),
    ]);

    expect(app(BaselineAutoCloseService::class)->shouldAutoClose($tenant, $run))->toBe($expected);
})->with('baseline auto close safety gates');
