<?php

declare(strict_types=1);

use App\Jobs\ExecuteRestoreRunJob;
use App\Models\BackupSet;
use App\Models\RestoreRun;
use App\Models\User;
use App\Services\OperationRunService;
use App\Support\Auth\Capabilities;
use App\Support\OperationRunOutcome;
use App\Support\RestoreRunStatus;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

function runQueuedRestoreJobThroughMiddleware(object $job, Closure $terminal): mixed
{
    $pipeline = array_reduce(
        array_reverse($job->middleware()),
        fn (Closure $next, object $middleware): Closure => fn (object $job): mixed => $middleware->handle($job, $next),
        $terminal,
    );

    return $pipeline($job);
}

it('blocks restore execution when the tenant becomes non-operable before start', function (): void {
    $tenant = \App\Models\ManagedEnvironment::factory()->create([
        'rbac_status' => 'ok',
        'rbac_last_checked_at' => now(),
    ]);
    $user = User::factory()->create();
    createUserWithTenant(tenant: $tenant, user: $user, role: 'owner', ensureDefaultMicrosoftProviderConnection: false);

    $backupSet = BackupSet::factory()->for($tenant)->create([
        'name' => 'Backup',
        'status' => 'completed',
        'item_count' => 0,
    ]);

    $restoreRun = RestoreRun::create([
        'managed_environment_id' => $tenant->id,
        'backup_set_id' => $backupSet->id,
        'requested_by' => 'actor@example.com',
        'is_dry_run' => false,
        'status' => RestoreRunStatus::Queued->value,
        'requested_items' => null,
        'preview' => [],
        'results' => null,
        'metadata' => [],
    ]);

    $tenant->delete();
    $tenant->refresh();

    $operationRun = app(OperationRunService::class)->ensureRun(
        tenant: $tenant,
        type: 'restore.execute',
        inputs: [
            'restore_run_id' => $restoreRun->id,
            'backup_set_id' => $backupSet->id,
            'is_dry_run' => false,
            'execution_authority_mode' => 'actor_bound',
            'required_capability' => Capabilities::TENANT_MANAGE,
        ],
        initiator: $user,
    );

    $tenant->forceFill([
        'status' => 'active',
        'deleted_at' => null,
    ])->save();

    $job = new ExecuteRestoreRunJob($restoreRun->id, 'actor@example.com', 'Actor', $operationRun);
    $terminalInvoked = false;

    runQueuedRestoreJobThroughMiddleware(
        $job,
        function (ExecuteRestoreRunJob $job) use (&$terminalInvoked): mixed {
            $terminalInvoked = true;

            return $job;
        },
    );

    $operationRun->refresh();
    $restoreRun->refresh();

    expect($terminalInvoked)->toBeFalse()
        ->and($operationRun->outcome)->toBe(OperationRunOutcome::Blocked->value)
        ->and($operationRun->context['reason_code'] ?? null)->toBe('tenant_not_operable')
        ->and($restoreRun->status)->toBe(RestoreRunStatus::Queued->value);
});

it('allows restore execution when legitimacy still holds', function (): void {
    $tenant = \App\Models\ManagedEnvironment::factory()->create([
        'rbac_status' => 'ok',
        'rbac_last_checked_at' => now(),
    ]);
    $user = User::factory()->create();
    createUserWithTenant(tenant: $tenant, user: $user, role: 'owner', ensureDefaultMicrosoftProviderConnection: false);

    $backupSet = BackupSet::factory()->for($tenant)->create([
        'name' => 'Backup',
        'status' => 'completed',
        'item_count' => 0,
    ]);

    $restoreRun = RestoreRun::create([
        'managed_environment_id' => $tenant->id,
        'backup_set_id' => $backupSet->id,
        'requested_by' => 'actor@example.com',
        'is_dry_run' => false,
        'status' => RestoreRunStatus::Queued->value,
        'requested_items' => null,
        'preview' => [],
        'results' => null,
        'metadata' => [],
    ]);

    $tenant->delete();
    $tenant->refresh();

    $operationRun = app(OperationRunService::class)->ensureRun(
        tenant: $tenant,
        type: 'restore.execute',
        inputs: [
            'restore_run_id' => $restoreRun->id,
            'backup_set_id' => $backupSet->id,
            'is_dry_run' => false,
            'execution_authority_mode' => 'actor_bound',
            'required_capability' => Capabilities::TENANT_MANAGE,
        ],
        initiator: $user,
    );

    $job = new ExecuteRestoreRunJob($restoreRun->id, 'actor@example.com', 'Actor', $operationRun);
    $terminalInvoked = false;

    runQueuedRestoreJobThroughMiddleware(
        $job,
        function (ExecuteRestoreRunJob $job) use (&$terminalInvoked): mixed {
            $terminalInvoked = true;

            return $job;
        },
    );

    $operationRun->refresh();

    expect($terminalInvoked)->toBeTrue()
        ->and($operationRun->outcome)->toBe(OperationRunOutcome::Succeeded->value);
});
