<?php

use App\Jobs\AddPoliciesToBackupSetJob;
use App\Models\BackupItem;
use App\Models\BackupSet;
use App\Models\BulkOperationRun;
use App\Models\Policy;
use App\Models\PolicyVersion;
use App\Services\BulkOperationService;
use App\Services\Intune\FoundationSnapshotService;
use App\Services\Intune\PolicyCaptureOrchestrator;
use App\Services\Intune\SnapshotValidator;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery\MockInterface;

uses(RefreshDatabase::class);

it('records stable failure reason codes and keeps run counts consistent', function () {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);

    $backupSet = BackupSet::factory()->create([
        'tenant_id' => $tenant->id,
        'name' => 'Test backup',
        'status' => 'completed',
        'metadata' => ['failures' => []],
    ]);

    $policyA = Policy::factory()->create([
        'tenant_id' => $tenant->id,
        'ignored_at' => null,
    ]);

    $policyB = Policy::factory()->create([
        'tenant_id' => $tenant->id,
        'ignored_at' => null,
    ]);

    $versionA = PolicyVersion::factory()->create([
        'tenant_id' => $tenant->id,
        'policy_id' => $policyA->id,
        'policy_type' => $policyA->policy_type,
        'platform' => $policyA->platform,
        'snapshot' => ['id' => $policyA->external_id],
    ]);

    $run = BulkOperationRun::factory()->create([
        'tenant_id' => $tenant->id,
        'user_id' => $user->id,
        'resource' => 'backup_set',
        'action' => 'add_policies',
        'status' => 'pending',
        'total_items' => 2,
        'item_ids' => [
            'backup_set_id' => $backupSet->id,
            'policy_ids' => [$policyA->id, $policyB->id],
            'options' => [
                'include_assignments' => true,
                'include_scope_tags' => true,
                'include_foundations' => false,
            ],
        ],
        'failures' => [],
    ]);

    $this->mock(PolicyCaptureOrchestrator::class, function (MockInterface $mock) use ($policyA, $policyB, $tenant, $versionA) {
        $mock->shouldReceive('capture')
            ->twice()
            ->andReturnUsing(function (
                Policy $policy,
                \App\Models\Tenant $tenantArg,
                bool $includeAssignments = false,
                bool $includeScopeTags = false,
                ?string $createdBy = null,
                array $metadata = []
            ) use ($policyA, $policyB, $tenant, $versionA) {
                expect($tenantArg->id)->toBe($tenant->id);
                expect($includeAssignments)->toBeTrue();
                expect($includeScopeTags)->toBeTrue();
                expect($metadata['backup_set_id'] ?? null)->not->toBeNull();

                if ($policy->is($policyA)) {
                    return [
                        'version' => $versionA,
                        'captured' => [
                            'payload' => [
                                'id' => $policyA->external_id,
                                '@odata.type' => '#microsoft.graph.deviceManagementConfigurationPolicy',
                            ],
                            'assignments' => [],
                            'scope_tags' => ['ids' => ['0'], 'names' => ['Default']],
                            'metadata' => [],
                        ],
                    ];
                }

                expect($policy->is($policyB))->toBeTrue();

                return [
                    'failure' => [
                        'policy_id' => $policyB->id,
                        'reason' => 'Forbidden',
                        'status' => 403,
                    ],
                ];
            });
    });

    $job = new AddPoliciesToBackupSetJob(
        bulkRunId: (int) $run->getKey(),
        backupSetId: (int) $backupSet->getKey(),
        includeAssignments: true,
        includeScopeTags: true,
        includeFoundations: false,
    );

    $job->handle(
        bulkOperationService: app(BulkOperationService::class),
        captureOrchestrator: app(PolicyCaptureOrchestrator::class),
        foundationSnapshots: $this->mock(FoundationSnapshotService::class),
        snapshotValidator: app(SnapshotValidator::class),
    );

    $run->refresh();
    $backupSet->refresh();

    expect($run->status)->toBe('completed_with_errors');
    expect($run->total_items)->toBe(2);
    expect($run->processed_items)->toBe(2);
    expect($run->succeeded)->toBe(1);
    expect($run->failed)->toBe(1);
    expect($run->skipped)->toBe(0);

    expect(BackupItem::query()
        ->where('backup_set_id', $backupSet->id)
        ->where('policy_id', $policyA->id)
        ->exists())->toBeTrue();

    $failureEntry = collect($run->failures ?? [])
        ->firstWhere('item_id', (string) $policyB->id);

    expect($failureEntry)->not->toBeNull();
    expect($failureEntry['reason_code'] ?? null)->toBe('graph_forbidden');

    expect($backupSet->status)->toBe('partial');
});
