<?php

use App\Jobs\RunInventorySyncJob;
use App\Models\InventorySyncRun;
use App\Notifications\OperationRunCompleted;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\Intune\AuditLogger;
use App\Services\Inventory\InventorySyncService;
use App\Services\OperationRunService;
use Mockery\MockInterface;

it('executes a pending inventory sync run and updates bulk progress + initiator attribution', function () {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $this->mock(GraphClientInterface::class, function (MockInterface $mock) {
        $mock->shouldReceive('listPolicies')
            ->atLeast()
            ->once()
            ->andReturn(new GraphResponse(true, [], 200));
    });

    $sync = app(InventorySyncService::class);
    $selectionPayload = $sync->defaultSelectionPayload();
    $computed = $sync->normalizeAndHashSelection($selectionPayload);
    $policyTypes = $computed['selection']['policy_types'];
    $run = $sync->createPendingRunForUser($tenant, $user, $computed['selection']);

    /** @var OperationRunService $opService */
    $opService = app(OperationRunService::class);
    $opRun = $opService->ensureRun(
        tenant: $tenant,
        type: 'inventory.sync',
        inputs: $computed['selection'],
        initiator: $user,
    );

    $job = new RunInventorySyncJob(
        tenantId: (int) $tenant->getKey(),
        userId: (int) $user->getKey(),
        inventorySyncRunId: (int) $run->getKey(),
        operationRun: $opRun,
    );

    $job->handle($sync, app(AuditLogger::class), $opService);

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

    expect($run->user_id)->toBe($user->id);
    expect($run->status)->toBe(InventorySyncRun::STATUS_SUCCESS);
    expect($run->started_at)->not->toBeNull();
    expect($run->finished_at)->not->toBeNull();

    expect($opRun->status)->toBe('completed');
    expect($opRun->outcome)->toBe('succeeded');

    $counts = is_array($opRun->summary_counts) ? $opRun->summary_counts : [];
    expect((int) ($counts['total'] ?? 0))->toBe(count($policyTypes));
    expect((int) ($counts['processed'] ?? 0))->toBe(count($policyTypes));
    expect((int) ($counts['succeeded'] ?? 0))->toBe(count($policyTypes));
    expect((int) ($counts['failed'] ?? 0))->toBe(0);

    expect($user->notifications()->count())->toBe(1);
    $this->assertDatabaseHas('notifications', [
        'notifiable_id' => $user->getKey(),
        'notifiable_type' => $user->getMorphClass(),
        'type' => OperationRunCompleted::class,
        'data->title' => 'Inventory sync completed',
    ]);
});

it('maps skipped inventory sync runs to bulk progress as skipped with reason', function () {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $sync = app(InventorySyncService::class);
    $selectionPayload = $sync->defaultSelectionPayload();
    $run = $sync->createPendingRunForUser($tenant, $user, $selectionPayload);

    $computed = $sync->normalizeAndHashSelection($selectionPayload);
    $policyTypes = $computed['selection']['policy_types'];

    $run->update(['selection_payload' => $computed['selection']]);

    /** @var OperationRunService $opService */
    $opService = app(OperationRunService::class);
    $opRun = $opService->ensureRun(
        tenant: $tenant,
        type: 'inventory.sync',
        inputs: $computed['selection'],
        initiator: $user,
    );

    $mockSync = \Mockery::mock(InventorySyncService::class);
    $mockSync
        ->shouldReceive('executePendingRun')
        ->once()
        ->andReturnUsing(function (InventorySyncRun $inventorySyncRun) {
            $inventorySyncRun->forceFill([
                'status' => InventorySyncRun::STATUS_SKIPPED,
                'error_codes' => ['locked'],
                'selection_payload' => $inventorySyncRun->selection_payload ?? [],
                'started_at' => now(),
                'finished_at' => now(),
            ])->save();

            return $inventorySyncRun;
        });

    $job = new RunInventorySyncJob(
        tenantId: (int) $tenant->getKey(),
        userId: (int) $user->getKey(),
        inventorySyncRunId: (int) $run->getKey(),
        operationRun: $opRun,
    );

    $job->handle($mockSync, app(AuditLogger::class), $opService);

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

    expect($run->status)->toBe(InventorySyncRun::STATUS_SKIPPED);

    expect($opRun->status)->toBe('completed');
    expect($opRun->outcome)->toBe('failed');

    $counts = is_array($opRun->summary_counts) ? $opRun->summary_counts : [];
    expect((int) ($counts['processed'] ?? 0))->toBe(count($policyTypes));
    expect((int) ($counts['skipped'] ?? 0))->toBe(count($policyTypes));
    expect((int) ($counts['succeeded'] ?? 0))->toBe(0);
    expect((int) ($counts['failed'] ?? 0))->toBe(0);

    $failures = is_array($opRun->failure_summary) ? $opRun->failure_summary : [];
    expect($failures)->toBeArray();
    expect($failures[0]['code'] ?? null)->toBe('inventory.skipped');
    expect($failures[0]['message'] ?? null)->toBe('locked');

    expect($user->notifications()->count())->toBe(1);
    $this->assertDatabaseHas('notifications', [
        'notifiable_id' => $user->getKey(),
        'notifiable_type' => $user->getMorphClass(),
        'type' => OperationRunCompleted::class,
        'data->title' => 'Inventory sync failed',
    ]);
});
