<?php

use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\Tenant;
use App\Services\Providers\ProviderOperationStartGate;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

it('starts a provider operation and dispatches the job once', function (): void {
    $tenant = Tenant::factory()->create();
    $connection = ProviderConnection::factory()->create([
        'tenant_id' => $tenant->getKey(),
        'provider' => 'microsoft',
        'entra_tenant_id' => 'entra-tenant-id',
    ]);

    $dispatched = 0;

    $gate = app(ProviderOperationStartGate::class);

    $result = $gate->start(
        tenant: $tenant,
        connection: $connection,
        operationType: 'provider.connection.check',
        dispatcher: function (OperationRun $run) use (&$dispatched): void {
            $dispatched++;
            expect($run->type)->toBe('provider.connection.check');
        },
    );

    expect($dispatched)->toBe(1);
    expect($result->status)->toBe('started');
    expect($result->dispatched)->toBeTrue();

    $run = $result->run->fresh();
    expect($run)->not->toBeNull();
    expect($run->type)->toBe('provider.connection.check');
    expect($run->status)->toBe('queued');

    expect($run->context)->toMatchArray([
        'provider' => 'microsoft',
        'module' => 'health_check',
        'provider_connection_id' => (int) $connection->getKey(),
        'target_scope' => [
            'entra_tenant_id' => 'entra-tenant-id',
        ],
    ]);
});

it('dedupes when the same operation is already active for the scope', function (): void {
    $tenant = Tenant::factory()->create();
    $connection = ProviderConnection::factory()->create([
        'tenant_id' => $tenant->getKey(),
    ]);

    $existing = OperationRun::factory()->create([
        'tenant_id' => $tenant->getKey(),
        'type' => 'provider.connection.check',
        'status' => 'running',
        'context' => [
            'provider_connection_id' => (int) $connection->getKey(),
        ],
    ]);

    $dispatched = 0;
    $gate = app(ProviderOperationStartGate::class);

    $result = $gate->start(
        tenant: $tenant,
        connection: $connection,
        operationType: 'provider.connection.check',
        dispatcher: function () use (&$dispatched): void {
            $dispatched++;
        },
    );

    expect($dispatched)->toBe(0);
    expect($result->status)->toBe('deduped');
    expect($result->run->getKey())->toBe($existing->getKey());
    expect(OperationRun::query()->where('tenant_id', $tenant->getKey())->count())->toBe(1);
});

it('blocks when a different operation is already active for the scope', function (): void {
    $tenant = Tenant::factory()->create();
    $connection = ProviderConnection::factory()->create([
        'tenant_id' => $tenant->getKey(),
    ]);

    $blocking = OperationRun::factory()->create([
        'tenant_id' => $tenant->getKey(),
        'type' => 'inventory.sync',
        'status' => 'queued',
        'context' => [
            'provider_connection_id' => (int) $connection->getKey(),
        ],
    ]);

    $dispatched = 0;
    $gate = app(ProviderOperationStartGate::class);

    $result = $gate->start(
        tenant: $tenant,
        connection: $connection,
        operationType: 'provider.connection.check',
        dispatcher: function () use (&$dispatched): void {
            $dispatched++;
        },
    );

    expect($dispatched)->toBe(0);
    expect($result->status)->toBe('scope_busy');
    expect($result->run->getKey())->toBe($blocking->getKey());
    expect(OperationRun::query()->where('tenant_id', $tenant->getKey())->count())->toBe(1);
});
