<?php

declare(strict_types=1);

use App\Filament\Resources\RestoreRunResource\Pages\CreateRestoreRun;
use App\Filament\System\Pages\Ops\Controls;
use App\Jobs\ExecuteRestoreRunJob;
use App\Models\AuditLog;
use App\Models\BackupItem;
use App\Models\BackupSet;
use App\Models\OperationalControlActivation;
use App\Models\OperationRun;
use App\Models\PlatformUser;
use App\Models\Policy;
use App\Models\RestoreRun;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Support\Audit\AuditActionId;
use App\Support\Auth\PlatformCapabilities;
use App\Support\RestoreRunStatus;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Bus;
use Livewire\Livewire;

uses(RefreshDatabase::class);

beforeEach(function (): void {
    putenv('INTUNE_TENANT_ID');
    unset($_ENV['INTUNE_TENANT_ID'], $_SERVER['INTUNE_TENANT_ID']);
});

function seedOperationalRestoreExecutionContext(bool $withProviderConnection = true, ?Workspace $workspace = null): array
{
    $workspace ??= Workspace::factory()->create(['name' => 'Restore Workspace']);

    $tenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'managed_environment_id' => fake()->uuid(),
        'name' => 'Restore ManagedEnvironment',
        'rbac_status' => 'ok',
        'rbac_last_checked_at' => now(),
    ]);

    if ($withProviderConnection) {
        ensureDefaultProviderConnection($tenant, 'microsoft');
        spec283SeedRequirementRows($tenant, ['permissions.intune_configuration', 'permissions.intune_rbac_assignments']);
    }

    $policy = Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => fake()->uuid(),
        'policy_type' => 'deviceConfiguration',
        'display_name' => 'Restore Policy',
        'platform' => 'windows',
    ]);

    $backupSet = BackupSet::create([
        'managed_environment_id' => $tenant->id,
        'name' => 'Restore Backup',
        'status' => 'completed',
        'item_count' => 1,
    ]);

    $backupItem = BackupItem::create([
        'managed_environment_id' => $tenant->id,
        'backup_set_id' => $backupSet->id,
        'policy_id' => $policy->id,
        'policy_identifier' => $policy->external_id,
        'policy_type' => $policy->policy_type,
        'platform' => $policy->platform,
        'payload' => ['id' => $policy->external_id],
        'metadata' => ['displayName' => 'Restore Policy'],
    ]);

    $user = User::factory()->create([
        'email' => fake()->unique()->safeEmail(),
        'name' => 'Restore Operator',
    ]);

    [$user, $tenant] = createUserWithTenant(tenant: $tenant, user: $user, role: 'owner');
    setAdminPanelContext($tenant);

    return [$tenant, $backupSet, $backupItem, $user, $workspace];
}

it('blocks restore execution before any operation run, restore run, job, or provider start is created', function (): void {
    Bus::fake();

    [$tenant, $backupSet, $backupItem, $user] = seedOperationalRestoreExecutionContext();

    OperationalControlActivation::factory()->workspaceScoped()->create([
        'control_key' => 'restore.execute',
        'workspace_id' => (int) $tenant->workspace_id,
        'reason_text' => 'Paused during restore safety review.',
    ]);

    $this->actingAs($user);

    Livewire::test(CreateRestoreRun::class)
        ->fillForm([
            'backup_set_id' => $backupSet->id,
        ])
        ->goToNextWizardStep()
        ->fillForm([
            'scope_mode' => 'selected',
            'backup_item_ids' => [$backupItem->id],
        ])
        ->goToNextWizardStep()
        ->callFormComponentAction('check_results', 'run_restore_checks')
        ->goToNextWizardStep()
        ->callFormComponentAction('preview_diffs', 'run_restore_preview')
        ->goToNextWizardStep()
        ->fillForm([
            'is_dry_run' => false,
            'acknowledged_impact' => true,
            'tenant_confirm' => 'Restore ManagedEnvironment',
        ])
        ->call('create')
        ->assertNotified('Restore execution paused');

    expect(RestoreRun::query()->count())->toBe(0)
        ->and(OperationRun::query()->where('type', 'restore.execute')->count())->toBe(0);

    $audit = AuditLog::query()
        ->where('action', AuditActionId::OperationalControlExecutionBlocked->value)
        ->latest('id')
        ->first();

    expect($audit)->not->toBeNull()
        ->and($audit?->workspace_id)->toBe((int) $tenant->workspace_id)
        ->and($audit?->managed_environment_id)->toBe((int) $tenant->getKey())
        ->and($audit?->status)->toBe('blocked')
        ->and($audit?->metadata['control_key'] ?? null)->toBe('restore.execute');

    Bus::assertNotDispatched(ExecuteRestoreRunJob::class);
});

it('does not retroactively mutate already accepted restore execution runs when a later pause is activated', function (): void {
    [$tenant, $backupSet, $backupItem, $user, $workspace] = seedOperationalRestoreExecutionContext(withProviderConnection: false);

    $operationRun = OperationRun::factory()
        ->forTenant($tenant)
        ->queued()
        ->create([
        'type' => 'restore.execute',
        'outcome' => 'pending',
        'initiator_name' => $user->name,
        'context' => [
            'backup_set_id' => (int) $backupSet->getKey(),
            'target_scope' => ['entra_tenant_id' => $tenant->graphTenantId()],
        ],
    ]);

    $restoreRun = RestoreRun::query()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'backup_set_id' => (int) $backupSet->getKey(),
        'operation_run_id' => (int) $operationRun->getKey(),
        'requested_by' => $user->email,
        'is_dry_run' => false,
        'status' => RestoreRunStatus::Queued->value,
        'idempotency_key' => 'accepted-before-pause',
        'requested_items' => [(int) $backupItem->getKey()],
        'preview' => ['summary' => []],
        'metadata' => ['confirmed_by' => $user->email],
        'group_mapping' => [],
    ]);

    $platformUser = PlatformUser::factory()->create([
        'capabilities' => [
            PlatformCapabilities::ACCESS_SYSTEM_PANEL,
            PlatformCapabilities::OPS_CONTROLS_MANAGE,
        ],
        'is_active' => true,
    ]);

    Filament::setCurrentPanel('system');
    Filament::bootCurrentPanel();
    $this->actingAs($platformUser, 'platform');

    Livewire::test(Controls::class)
        ->callAction('pause_restore_execute', data: [
            'scope_type' => 'workspace',
            'workspace_id' => (int) $workspace->getKey(),
            'reason_text' => 'Pause after the run was already accepted.',
            'expires_at' => now()->addHour()->toDateTimeString(),
        ])
        ->assertNotified('Restore execution paused');

    expect($operationRun->fresh())
        ->not->toBeNull()
        ->and($operationRun->fresh()?->status)->toBe('queued')
        ->and($operationRun->fresh()?->outcome)->toBe('pending');

    expect($restoreRun->fresh())
        ->not->toBeNull()
        ->and($restoreRun->fresh()?->status)->toBe(RestoreRunStatus::Queued->value)
        ->and((int) ($restoreRun->fresh()?->operation_run_id ?? 0))->toBe((int) $operationRun->getKey());
});

it('does not block restore execution for a different workspace when the pause is workspace-scoped', function (): void {
    Bus::fake();

    $blockedWorkspace = Workspace::factory()->create(['name' => 'Blocked Workspace']);
    $allowedWorkspace = Workspace::factory()->create(['name' => 'Allowed Workspace']);

    [$blockedTenant] = seedOperationalRestoreExecutionContext(workspace: $blockedWorkspace);
    [$allowedTenant, $backupSet, $backupItem, $user] = seedOperationalRestoreExecutionContext(workspace: $allowedWorkspace);

    OperationalControlActivation::factory()->workspaceScoped()->create([
        'control_key' => 'restore.execute',
        'workspace_id' => (int) $blockedTenant->workspace_id,
        'reason_text' => 'Paused only for the blocked workspace.',
    ]);

    $this->actingAs($user);
    setAdminPanelContext($allowedTenant);

    Livewire::test(CreateRestoreRun::class)
        ->fillForm([
            'backup_set_id' => $backupSet->id,
        ])
        ->goToNextWizardStep()
        ->fillForm([
            'scope_mode' => 'selected',
            'backup_item_ids' => [$backupItem->id],
        ])
        ->goToNextWizardStep()
        ->callFormComponentAction('check_results', 'run_restore_checks')
        ->goToNextWizardStep()
        ->callFormComponentAction('preview_diffs', 'run_restore_preview')
        ->goToNextWizardStep()
        ->fillForm([
            'is_dry_run' => false,
            'acknowledged_impact' => true,
            'tenant_confirm' => 'Restore ManagedEnvironment',
        ])
        ->call('create')
        ->assertHasNoFormErrors();

    $restoreRun = RestoreRun::query()
        ->where('managed_environment_id', (int) $allowedTenant->getKey())
        ->latest('id')
        ->first();

    $operationRun = OperationRun::query()
        ->where('managed_environment_id', (int) $allowedTenant->getKey())
        ->where('type', 'restore.execute')
        ->latest('id')
        ->first();

    expect($restoreRun)->not->toBeNull()
        ->and($operationRun)->not->toBeNull();

    Bus::assertDispatched(ExecuteRestoreRunJob::class);
});
