<?php

declare(strict_types=1);

use App\Jobs\GeneratePermissionPostureFindingsJob;
use App\Models\Finding;
use App\Models\OperationRun;
use App\Models\StoredReport;
use App\Services\PermissionPosture\FindingGeneratorContract;
use App\Support\OperationCatalog;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

it('full posture check flow: findings, report, score, operations', function (): void {
    [$user, $tenant] = createUserWithTenant(fixtureProfile: 'provider-enabled');

    $comparison = [
        'overall_status' => 'missing',
        'permissions' => [
            ['key' => 'DeviceManagementConfiguration.ReadWrite.All', 'type' => 'application', 'status' => 'missing', 'features' => ['backup', 'restore']],
            ['key' => 'DeviceManagementApps.ReadWrite.All', 'type' => 'application', 'status' => 'granted', 'features' => ['app_management']],
            ['key' => 'DeviceManagementManagedDevices.ReadWrite.All', 'type' => 'application', 'status' => 'missing', 'features' => ['compliance']],
            ['key' => 'Group.Read.All', 'type' => 'application', 'status' => 'granted', 'features' => ['assignments']],
            ['key' => 'User.Read', 'type' => 'delegated', 'status' => 'granted', 'features' => []],
        ],
        'last_refreshed_at' => now()->toIso8601String(),
    ];

    $start = microtime(true);

    $job = new GeneratePermissionPostureFindingsJob($tenant->getKey(), $comparison);
    $job->handle(
        app(FindingGeneratorContract::class),
        app(\App\Services\OperationRunService::class),
    );

    $elapsed = (microtime(true) - $start) * 1000;

    // --- Timing assertion ---
    expect($elapsed)->toBeLessThan(5000);

    // --- Findings ---
    $findings = Finding::query()
        ->where('managed_environment_id', $tenant->getKey())
        ->where('finding_type', Finding::FINDING_TYPE_PERMISSION_POSTURE)
        ->get();

    $missingFindings = $findings->where('status', Finding::STATUS_NEW);
    expect($missingFindings)->toHaveCount(2); // 2 missing permissions

    // --- StoredReport ---
    $report = StoredReport::query()
        ->where('managed_environment_id', $tenant->getKey())
        ->where('report_type', StoredReport::REPORT_TYPE_PERMISSION_POSTURE)
        ->first();

    expect($report)->not->toBeNull()
        ->and($report->payload['posture_score'])->toBe(60) // 3/5 = 60%
        ->and($report->payload['required_count'])->toBe(5)
        ->and($report->payload['granted_count'])->toBe(3)
        ->and($report->payload['permissions'])->toHaveCount(5);

    // --- OperationRun ---
    $run = OperationRun::query()
        ->where('managed_environment_id', $tenant->getKey())
        ->where('type', OperationCatalog::TYPE_PERMISSION_POSTURE_CHECK)
        ->first();

    expect($run)->not->toBeNull()
        ->and($run->status)->toBe(OperationRunStatus::Completed->value)
        ->and($run->outcome)->toBe(OperationRunOutcome::Succeeded->value)
        ->and($run->summary_counts['findings_created'])->toBe(2)
        ->and($run->summary_counts['posture_score'])->toBe(60);
});

it('second run auto-resolves findings for newly granted permissions', function (): void {
    [$user, $tenant] = createUserWithTenant(fixtureProfile: 'provider-enabled');

    $generator = app(FindingGeneratorContract::class);

    // First run: 2 missing permissions
    $comparison1 = [
        'overall_status' => 'missing',
        'permissions' => [
            ['key' => 'Perm.A', 'type' => 'application', 'status' => 'missing', 'features' => ['a']],
            ['key' => 'Perm.B', 'type' => 'application', 'status' => 'missing', 'features' => ['b']],
            ['key' => 'Perm.C', 'type' => 'application', 'status' => 'granted', 'features' => ['c']],
        ],
        'last_refreshed_at' => now()->toIso8601String(),
    ];

    $result1 = $generator->generate($tenant, $comparison1);
    expect($result1->findingsCreated)->toBe(2)
        ->and($result1->postureScore)->toBe(33);

    // Second run: Perm.A now granted
    $comparison2 = [
        'overall_status' => 'missing',
        'permissions' => [
            ['key' => 'Perm.A', 'type' => 'application', 'status' => 'granted', 'features' => ['a']],
            ['key' => 'Perm.B', 'type' => 'application', 'status' => 'missing', 'features' => ['b']],
            ['key' => 'Perm.C', 'type' => 'application', 'status' => 'granted', 'features' => ['c']],
        ],
        'last_refreshed_at' => now()->toIso8601String(),
    ];

    $result2 = $generator->generate($tenant, $comparison2);
    expect($result2->findingsResolved)->toBe(1)
        ->and($result2->findingsUnchanged)->toBe(1)
        ->and($result2->postureScore)->toBe(67);

    // Verify the resolved finding
    $resolvedFinding = Finding::query()
        ->where('managed_environment_id', $tenant->getKey())
        ->where('finding_type', Finding::FINDING_TYPE_PERMISSION_POSTURE)
        ->where('status', Finding::STATUS_RESOLVED)
        ->first();

    expect($resolvedFinding)->not->toBeNull()
        ->and($resolvedFinding->resolved_reason)->toBe('permission_granted');
});

it('alert events are produced for new missing permission findings', function (): void {
    [$user, $tenant] = createUserWithTenant(fixtureProfile: 'provider-enabled');

    $generator = app(FindingGeneratorContract::class);

    $comparison = [
        'overall_status' => 'missing',
        'permissions' => [
            ['key' => 'Perm.Dangerous', 'type' => 'application', 'status' => 'missing', 'features' => ['backup', 'restore', 'sync']],
        ],
        'last_refreshed_at' => now()->toIso8601String(),
    ];

    $result = $generator->generate($tenant, $comparison);
    expect($result->findingsCreated)->toBe(1);

    // Verify the finding was created and has proper data for alert pipeline
    $finding = Finding::query()
        ->where('managed_environment_id', $tenant->getKey())
        ->where('finding_type', Finding::FINDING_TYPE_PERMISSION_POSTURE)
        ->where('status', Finding::STATUS_NEW)
        ->first();

    expect($finding)->not->toBeNull()
        ->and($finding->evidence_jsonb)->toHaveKey('permission_key')
        ->and($finding->evidence_jsonb['permission_key'])->toBe('Perm.Dangerous');
});
