<?php

declare(strict_types=1);

use App\Models\InventoryItem;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Support\Inventory\TenantCoverageTruthResolver;
use App\Support\Inventory\TenantCoverageTypeTruth;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

it('selects the latest completed coverage-bearing inventory sync run for tenant truth', function (): void {
    $tenant = ManagedEnvironment::factory()->create();

    $olderBasis = createInventorySyncOperationRunWithCoverage($tenant, [
        'deviceConfiguration' => ['status' => 'succeeded', 'item_count' => 1],
    ], attributes: [
        'completed_at' => now()->subHours(2),
        'outcome' => 'succeeded',
    ]);

    createInventorySyncOperationRun($tenant, [
        'status' => 'completed',
        'outcome' => 'failed',
        'completed_at' => now()->subMinute(),
    ]);

    $latestCoverageBearing = createInventorySyncOperationRunWithCoverage($tenant, [
        'deviceConfiguration' => ['status' => 'failed', 'item_count' => 2, 'error_code' => 'graph_forbidden'],
    ], attributes: [
        'completed_at' => now()->subMinutes(5),
        'outcome' => 'failed',
    ]);

    expect(OperationRun::latestCompletedCoverageBearingInventorySyncForTenant((int) $tenant->getKey())?->is($latestCoverageBearing))
        ->toBeTrue()
        ->and(OperationRun::latestCompletedCoverageBearingInventorySyncForTenant((int) $tenant->getKey())?->is($olderBasis))
        ->toBeFalse();

    $truth = app(TenantCoverageTruthResolver::class)->resolve($tenant);

    expect($truth->basisRunId())->toBe((int) $latestCoverageBearing->getKey())
        ->and($truth->basisRunOutcome())->toBe('failed')
        ->and($truth->hasCurrentCoverageResult)->toBeTrue();
});

it('derives per-type coverage truth, observed counts, and deterministic follow-up order', function (): void {
    $tenant = ManagedEnvironment::factory()->create();

    InventoryItem::factory()->count(3)->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'policy_type' => 'deviceCompliancePolicy',
    ]);

    InventoryItem::factory()->count(2)->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'policy_type' => 'conditionalAccessPolicy',
    ]);

    InventoryItem::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'policy_type' => 'deviceConfiguration',
    ]);

    createInventorySyncOperationRunWithCoverage($tenant, [
        'deviceCompliancePolicy' => [
            'status' => 'failed',
            'item_count' => 3,
            'error_code' => 'graph_forbidden',
        ],
        'deviceConfiguration' => [
            'status' => 'succeeded',
            'item_count' => 1,
        ],
        'roleScopeTag' => [
            'status' => 'skipped',
            'item_count' => 0,
        ],
    ], foundationTypes: ['roleScopeTag'], attributes: [
        'completed_at' => now(),
        'outcome' => 'partially_succeeded',
    ]);

    $truth = app(TenantCoverageTruthResolver::class)->resolve($tenant);

    $rowsByType = collect($truth->rows)->keyBy(
        static fn (TenantCoverageTypeTruth $row): string => $row->type,
    );

    expect($truth->supportedTypeCount)->toBeGreaterThanOrEqual(4)
        ->and($truth->succeededTypeCount)->toBe(1)
        ->and($truth->failedTypeCount)->toBe(1)
        ->and($truth->skippedTypeCount)->toBe(1)
        ->and($truth->unknownTypeCount)->toBe($truth->supportedTypeCount - 3)
        ->and($truth->followUpTypeCount)->toBe($truth->supportedTypeCount - 1)
        ->and($truth->observedItemTotal)->toBe(6);

    expect($rowsByType['deviceCompliancePolicy'])
        ->coverageState->toBe('failed')
        ->basisErrorCode->toBe('graph_forbidden')
        ->basisItemCount->toBe(3)
        ->followUpGuidance->toBe('Review provider consent or permissions, then rerun inventory sync.');

    expect($rowsByType['conditionalAccessPolicy'])
        ->coverageState->toBe('unknown')
        ->observedItemCount->toBe(2)
        ->followUpGuidance->toBe('No current basis result exists for this type. Run inventory sync to confirm coverage.');

    expect($rowsByType['deviceConfiguration'])
        ->coverageState->toBe('succeeded')
        ->basisItemCount->toBe(1)
        ->followUpRequired->toBeFalse();

    expect($rowsByType['roleScopeTag'])
        ->coverageState->toBe('skipped')
        ->segment->toBe('foundation')
        ->followUpGuidance->toBe('Run inventory sync again with the required types selected.');

    $orderedTypes = array_map(
        static fn (TenantCoverageTypeTruth $row): string => $row->type,
        $truth->rows,
    );

    expect(array_search('deviceCompliancePolicy', $orderedTypes, true))->toBeLessThan(array_search('conditionalAccessPolicy', $orderedTypes, true))
        ->and(array_search('conditionalAccessPolicy', $orderedTypes, true))->toBeLessThan(array_search('roleScopeTag', $orderedTypes, true))
        ->and($truth->topPriorityFollowUpRow()?->type)->toBe('deviceCompliancePolicy');
});

it('returns unknown coverage rows when no basis run exists', function (): void {
    $tenant = ManagedEnvironment::factory()->create();

    InventoryItem::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'policy_type' => 'deviceConfiguration',
    ]);

    $truth = app(TenantCoverageTruthResolver::class)->resolve($tenant);

    expect($truth->hasCurrentCoverageResult)->toBeFalse()
        ->and($truth->basisRunId())->toBeNull()
        ->and($truth->succeededTypeCount)->toBe(0)
        ->and($truth->failedTypeCount)->toBe(0)
        ->and($truth->skippedTypeCount)->toBe(0)
        ->and($truth->unknownTypeCount)->toBe($truth->supportedTypeCount)
        ->and($truth->followUpTypeCount)->toBe($truth->supportedTypeCount)
        ->and($truth->observedItemTotal)->toBe(1)
        ->and($truth->topPriorityFollowUpRow())->not->toBeNull();
});
