<?php

namespace App\Filament\Resources;

use App\Contracts\Hardening\WriteGateInterface;
use App\Exceptions\Hardening\ProviderAccessHardeningRequired;
use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\RestoreRunResource\Pages;
use App\Jobs\BulkRestoreRunDeleteJob;
use App\Jobs\BulkRestoreRunForceDeleteJob;
use App\Jobs\BulkRestoreRunRestoreJob;
use App\Jobs\ExecuteRestoreRunJob;
use App\Models\BackupItem;
use App\Models\BackupSet;
use App\Models\EntraGroup;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\RestoreRun;
use App\Models\User;
use App\Models\Workspace;
use App\Rules\SkipOrUuidRule;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\Auth\CapabilityResolver;
use App\Services\Directory\EntraGroupLabelResolver;
use App\Services\Intune\AuditLogger;
use App\Services\Intune\RestoreDiffGenerator;
use App\Services\Intune\RestoreRiskChecker;
use App\Services\Intune\RestoreService;
use App\Services\OperationRunService;
use App\Services\Operations\BulkSelectionIdentity;
use App\Services\Providers\ProviderOperationStartGate;
use App\Services\Providers\ProviderOperationStartResult;
use App\Support\Audit\AuditActionId;
use App\Support\Auth\Capabilities;
use App\Support\BackupQuality\BackupQualityResolver;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Filament\FilterOptionCatalog;
use App\Support\Filament\FilterPresets;
use App\Support\Navigation\NavigationScope;
use App\Support\OperationalControls\OperationalControlBlockedException;
use App\Support\OperationalControls\OperationalControlEvaluator;
use App\Support\OperationCatalog;
use App\Support\OperationRunLinks;
use App\Support\OpsUx\OperationUxPresenter;
use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\OpsUx\ProviderOperationStartResultPresenter;
use App\Support\Rbac\UiEnforcement;
use App\Support\RestoreRunIdempotency;
use App\Support\RestoreRunStatus;
use App\Support\RestoreSafety\ChecksIntegrityState;
use App\Support\RestoreSafety\PreviewIntegrityState;
use App\Support\RestoreSafety\RestoreSafetyCopy;
use App\Support\RestoreSafety\RestoreSafetyResolver;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
use BackedEnum;
use Filament\Actions;
use Filament\Actions\ActionGroup;
use Filament\Actions\BulkAction;
use Filament\Actions\BulkActionGroup;
use Filament\Forms;
use Filament\Infolists;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Utilities\Set;
use Filament\Schemas\Components\Wizard\Step;
use Filament\Schemas\Schema;
use Filament\Tables;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use UnitEnum;

class RestoreRunResource extends Resource
{
    use InteractsWithTenantOwnedRecords;
    use ResolvesPanelTenantContext;
    use WorkspaceScopedEnvironmentRoutes;

    protected static ?string $model = RestoreRun::class;

    protected static ?string $tenantOwnershipRelationshipName = 'tenant';

    public static function shouldRegisterNavigation(): bool
    {
        return NavigationScope::shouldRegisterEnvironmentNavigation()
            && parent::shouldRegisterNavigation();
    }

    protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-arrow-path-rounded-square';

    protected static string|UnitEnum|null $navigationGroup = 'Backups & Restore';

    public static function canViewAny(): bool
    {
        $tenant = static::resolveTenantContextForCurrentPanel();
        $user = auth()->user();

        if (! $tenant instanceof ManagedEnvironment || ! $user instanceof User) {
            return false;
        }

        /** @var CapabilityResolver $resolver */
        $resolver = app(CapabilityResolver::class);

        return $resolver->isMember($user, $tenant)
            && $resolver->can($user, $tenant, Capabilities::TENANT_VIEW);
    }

    public static function canCreate(): bool
    {
        $tenant = static::resolveTenantContextForCurrentPanel();
        $user = auth()->user();

        if (! $tenant instanceof ManagedEnvironment || ! $user instanceof User) {
            return false;
        }

        /** @var CapabilityResolver $resolver */
        $resolver = app(CapabilityResolver::class);

        return $resolver->isMember($user, $tenant)
            && $resolver->can($user, $tenant, Capabilities::TENANT_MANAGE);
    }

    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forResource(ActionSurfaceProfile::CrudListAndView)
            ->satisfy(ActionSurfaceSlot::ListHeader, 'Create restore run is available from the list header whenever records already exist.')
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value)
            ->satisfy(ActionSurfaceSlot::ListRowMoreMenu, 'Clickable-row inspection stays primary while rerun and archive lifecycle actions stay grouped under More.')
            ->satisfy(ActionSurfaceSlot::ListBulkMoreGroup, 'Bulk restore-run maintenance actions are grouped under More.')
            ->satisfy(ActionSurfaceSlot::ListEmptyState, 'Empty state exposes the New restore run CTA.')
            ->exempt(ActionSurfaceSlot::DetailHeader, 'View page has no additional header actions.');
    }

    public static function form(Schema $schema): Schema
    {
        return $schema
            ->schema([
                Forms\Components\Select::make('backup_set_id')
                    ->label('Backup set')
                    ->options(fn () => static::restoreBackupSetOptions())
                    ->helperText(fn (Get $get): string => static::restoreBackupSetHelperText($get('backup_set_id')))
                    ->reactive()
                    ->afterStateUpdated(function (Set $set): void {
                        $set('scope_mode', 'all');
                        $set('backup_item_ids', null);
                        $set('group_mapping', []);
                        $set('is_dry_run', true);
                    })
                    ->required(),
                Forms\Components\CheckboxList::make('backup_item_ids')
                    ->label('Items to restore (optional)')
                    ->options(fn (Get $get) => static::restoreItemOptionData($get('backup_set_id'))['options'])
                    ->descriptions(fn (Get $get) => static::restoreItemOptionData($get('backup_set_id'))['descriptions'])
                    ->columns(1)
                    ->searchable()
                    ->bulkToggleable()
                    ->reactive()
                    ->afterStateUpdated(fn (Set $set) => $set('group_mapping', []))
                    ->helperText(fn (): string => static::restoreItemQualityHelperText()),
                Section::make('Group mapping')
                    ->description('Some source groups do not exist in the target tenant. Map them or choose Skip.')
                    ->schema(function (Get $get): array {
                        $backupSetId = $get('backup_set_id');
                        $selectedItemIds = $get('backup_item_ids');
                        $tenant = static::resolveTenantContextForCurrentPanel();

                        if (! $tenant || ! $backupSetId) {
                            return [];
                        }

                        $unresolved = static::unresolvedGroups(
                            backupSetId: $backupSetId,
                            selectedItemIds: is_array($selectedItemIds) ? $selectedItemIds : null,
                            tenant: $tenant
                        );

                        $groupCacheQuery = EntraGroup::query()->where('managed_environment_id', $tenant->getKey());
                        $hasCachedGroups = $groupCacheQuery->exists();

                        $stalenessDays = (int) config('directory_groups.staleness_days', 30);
                        $cutoff = now('UTC')->subDays(max(1, $stalenessDays));
                        $latestSeen = $groupCacheQuery->max('last_seen_at');
                        $isStale = $hasCachedGroups && (! $latestSeen || $latestSeen < $cutoff);

                        $cacheNotice = match (true) {
                            ! $hasCachedGroups => 'No cached groups found. Run "Sync Groups" first.',
                            $isStale => "Cached groups may be stale (>{$stalenessDays} days). Consider running \"Sync Groups\".",
                            default => null,
                        };

                        return array_map(function (array $group) use ($cacheNotice): Forms\Components\TextInput {
                            $groupId = $group['id'];
                            $label = $group['label'];

                            return Forms\Components\TextInput::make("group_mapping.{$groupId}")
                                ->label($label)
                                ->placeholder('SKIP or target group Object ID (GUID)')
                                ->rules([new SkipOrUuidRule])
                                ->required()
                                ->suffixAction(
                                    Actions\Action::make('select_from_directory_cache_'.str_replace('-', '_', $groupId))
                                        ->icon('heroicon-o-magnifying-glass')
                                        ->iconButton()
                                        ->tooltip('Select from Directory cache')
                                        ->modalHeading('Select from Directory cache')
                                        ->modalWidth('5xl')
                                        ->modalSubmitAction(false)
                                        ->modalCancelActionLabel('Close')
                                        ->modalContent(fn () => view('filament.modals.entra-group-cache-picker', [
                                            'sourceGroupId' => $groupId,
                                        ]))
                                )
                                ->helperText(fn (): string => ($cacheNotice ? ($cacheNotice.' ') : '').'Paste the target Entra ID group Object ID (GUID). Labels use cached directory groups only (no live Graph lookups). Use SKIP to omit the assignment.')
                                ->hintAction(
                                    Actions\Action::make('open_directory_groups_'.str_replace('-', '_', $groupId))
                                        ->label('Sync Groups')
                                        ->icon('heroicon-o-arrow-path')
                                        ->color('warning')
                                        ->url(fn (): string => EntraGroupResource::getUrl('index', tenant: static::resolveTenantContextForCurrentPanel()))
                                        ->visible(fn (): bool => $cacheNotice !== null)
                                );
                        }, $unresolved);
                    })
                    ->visible(function (Get $get): bool {
                        $backupSetId = $get('backup_set_id');
                        $selectedItemIds = $get('backup_item_ids');
                        $tenant = static::resolveTenantContextForCurrentPanel();

                        if (! $tenant || ! $backupSetId) {
                            return false;
                        }

                        return static::unresolvedGroups(
                            backupSetId: $backupSetId,
                            selectedItemIds: is_array($selectedItemIds) ? $selectedItemIds : null,
                            tenant: $tenant
                        ) !== [];
                    }),
                Forms\Components\Toggle::make('is_dry_run')
                    ->label('Preview only (dry-run)')
                    ->default(true),
            ]);
    }

    public static function makeCreateAction(): Actions\CreateAction
    {
        $action = Actions\CreateAction::make()
            ->label('New restore run');

        UiEnforcement::forAction($action)
            ->requireCapability(Capabilities::TENANT_MANAGE)
            ->apply();

        return $action;
    }

    public static function getEloquentQuery(): Builder
    {
        return static::scopeTenantOwnedQuery(parent::getEloquentQuery())
            ->with(['backupSet', 'operationRun']);
    }

    public static function resolveScopedRecordOrFail(int|string $key): Model
    {
        return static::resolveTenantOwnedRecordOrFail(
            $key,
            parent::getEloquentQuery()->withTrashed()->with('backupSet'),
        );
    }

    protected static function resolveProtectedRestoreRunRecordOrFail(RestoreRun|int|string $record): RestoreRun
    {
        $resolvedRecord = static::resolveScopedRecordOrFail($record instanceof Model ? $record->getKey() : $record);

        if (! $resolvedRecord instanceof RestoreRun) {
            abort(404);
        }

        return $resolvedRecord;
    }

    /**
     * @return array<int, int>
     */
    protected static function resolveProtectedRestoreRunIds(Collection $records): array
    {
        return $records
            ->map(function (mixed $record): int {
                $resolvedRecord = static::resolveProtectedRestoreRunRecordOrFail($record instanceof RestoreRun ? $record : (is_numeric($record) ? (int) $record : 0));

                return (int) $resolvedRecord->getKey();
            })
            ->filter(fn (int $value): bool => $value > 0)
            ->unique()
            ->values()
            ->all();
    }

    /**
     * @return array<int, Step>
     */
    public static function getWizardSteps(): array
    {
        return [
            Step::make('Select Backup Set')
                ->description('What are we restoring from? Backup quality is visible here before safety checks run.')
                ->schema([
                    Forms\Components\Select::make('backup_set_id')
                        ->label('Backup set')
                        ->options(fn () => static::restoreBackupSetOptions())
                        ->helperText(fn (Get $get): string => static::restoreBackupSetHelperText($get('backup_set_id')))
                        ->reactive()
                        ->afterStateUpdated(function (Set $set, Get $get): void {
                            $groupMapping = static::groupMappingPlaceholders(
                                backupSetId: $get('backup_set_id'),
                                scopeMode: 'all',
                                selectedItemIds: null,
                                tenant: static::resolveTenantContextForCurrentPanel(),
                            );

                            $set('scope_mode', 'all');
                            $set('backup_item_ids', null);
                            $set('group_mapping', $groupMapping);
                            $set('is_dry_run', true);
                            $set('acknowledged_impact', false);
                            $set('tenant_confirm', null);

                            $draft = static::synchronizeRestoreSafetyDraft([
                                ...static::draftDataSnapshot($get),
                                'scope_mode' => 'all',
                                'backup_item_ids' => [],
                                'group_mapping' => $groupMapping,
                            ]);

                            $set('scope_basis', $draft['scope_basis']);
                            $set('check_invalidation_reasons', $draft['check_invalidation_reasons']);
                            $set('preview_invalidation_reasons', $draft['preview_invalidation_reasons']);
                        })
                        ->required(),
                ]),
            Step::make('Define Restore Scope')
                ->description('What exactly should be restored? Item quality hints appear here before restore risk checks.')
                ->schema([
                    Forms\Components\Radio::make('scope_mode')
                        ->label('Scope')
                        ->options([
                            'all' => 'All items (default)',
                            'selected' => 'Selected items only',
                        ])
                        ->default('all')
                        ->reactive()
                        ->afterStateUpdated(function (Set $set, Get $get, $state): void {
                            $backupSetId = $get('backup_set_id');
                            $tenant = static::resolveTenantContextForCurrentPanel();
                            $set('is_dry_run', true);
                            $set('acknowledged_impact', false);
                            $set('tenant_confirm', null);

                            if ($state === 'all') {
                                $groupMapping = static::groupMappingPlaceholders(
                                    backupSetId: $backupSetId,
                                    scopeMode: 'all',
                                    selectedItemIds: null,
                                    tenant: $tenant,
                                );

                                $set('backup_item_ids', null);
                                $set('group_mapping', $groupMapping);

                                $draft = static::synchronizeRestoreSafetyDraft([
                                    ...static::draftDataSnapshot($get),
                                    'scope_mode' => 'all',
                                    'backup_item_ids' => [],
                                    'group_mapping' => $groupMapping,
                                ]);

                                $set('scope_basis', $draft['scope_basis']);
                                $set('check_invalidation_reasons', $draft['check_invalidation_reasons']);
                                $set('preview_invalidation_reasons', $draft['preview_invalidation_reasons']);

                                return;
                            }

                            $set('group_mapping', []);
                            $set('backup_item_ids', []);

                            $draft = static::synchronizeRestoreSafetyDraft([
                                ...static::draftDataSnapshot($get),
                                'scope_mode' => 'selected',
                                'backup_item_ids' => [],
                                'group_mapping' => [],
                            ]);

                            $set('scope_basis', $draft['scope_basis']);
                            $set('check_invalidation_reasons', $draft['check_invalidation_reasons']);
                            $set('preview_invalidation_reasons', $draft['preview_invalidation_reasons']);
                        })
                        ->required(),
                    Forms\Components\Select::make('backup_item_ids')
                        ->label('Items to restore')
                        ->multiple()
                        ->searchable()
                        ->searchValues()
                        ->searchDebounce(400)
                        ->optionsLimit(300)
                        ->options(fn (Get $get) => static::restoreItemGroupedOptions($get('backup_set_id')))
                        ->reactive()
                        ->afterStateUpdated(function (Set $set, Get $get): void {
                            $backupSetId = $get('backup_set_id');
                            $selectedItemIds = $get('backup_item_ids');
                            $selectedItemIds = is_array($selectedItemIds) ? $selectedItemIds : null;
                            $tenant = static::resolveTenantContextForCurrentPanel();

                            $set('group_mapping', static::groupMappingPlaceholders(
                                backupSetId: $backupSetId,
                                scopeMode: 'selected',
                                selectedItemIds: $selectedItemIds,
                                tenant: $tenant,
                            ));
                            $set('is_dry_run', true);
                            $set('acknowledged_impact', false);
                            $set('tenant_confirm', null);

                            $draft = static::synchronizeRestoreSafetyDraft([
                                ...static::draftDataSnapshot($get),
                                'backup_item_ids' => $selectedItemIds ?? [],
                                'group_mapping' => static::groupMappingPlaceholders(
                                    backupSetId: $backupSetId,
                                    scopeMode: 'selected',
                                    selectedItemIds: $selectedItemIds,
                                    tenant: $tenant,
                                ),
                            ]);

                            $set('scope_basis', $draft['scope_basis']);
                            $set('check_invalidation_reasons', $draft['check_invalidation_reasons']);
                            $set('preview_invalidation_reasons', $draft['preview_invalidation_reasons']);
                        })
                        ->visible(fn (Get $get): bool => $get('scope_mode') === 'selected')
                        ->required(fn (Get $get): bool => $get('scope_mode') === 'selected')
                        ->hintActions([
                            Actions\Action::make('select_all_backup_items')
                                ->label('Select all')
                                ->icon('heroicon-o-check')
                                ->color('gray')
                                ->visible(fn (Get $get): bool => filled($get('backup_set_id')) && $get('scope_mode') === 'selected')
                                ->action(function (Get $get, Set $set): void {
                                    $groupedOptions = static::restoreItemGroupedOptions($get('backup_set_id'));

                                    $allItemIds = [];

                                    foreach ($groupedOptions as $options) {
                                        $allItemIds = array_merge($allItemIds, array_keys($options));
                                    }

                                    $set('backup_item_ids', array_values($allItemIds), shouldCallUpdatedHooks: true);
                                }),
                            Actions\Action::make('clear_backup_items')
                                ->label('Clear')
                                ->icon('heroicon-o-x-mark')
                                ->color('gray')
                                ->visible(fn (Get $get): bool => $get('scope_mode') === 'selected')
                                ->action(fn (Set $set) => $set('backup_item_ids', [], shouldCallUpdatedHooks: true)),
                        ])
                        ->helperText(fn (): string => static::restoreItemQualityHelperText()),
                    Section::make('Group mapping')
                        ->description('Some source groups do not exist in the target tenant. Map them or choose Skip.')
                        ->schema(function (Get $get): array {
                            $backupSetId = $get('backup_set_id');
                            $scopeMode = $get('scope_mode') ?? 'all';
                            $selectedItemIds = $get('backup_item_ids');
                            $tenant = static::resolveTenantContextForCurrentPanel();

                            if (! $tenant || ! $backupSetId) {
                                return [];
                            }

                            $selectedItemIds = is_array($selectedItemIds) ? $selectedItemIds : null;

                            if ($scopeMode === 'selected' && ($selectedItemIds === null || $selectedItemIds === [])) {
                                return [];
                            }

                            $unresolved = static::unresolvedGroups(
                                backupSetId: $backupSetId,
                                selectedItemIds: $scopeMode === 'selected' ? $selectedItemIds : null,
                                tenant: $tenant
                            );

                            $groupCacheQuery = EntraGroup::query()->where('managed_environment_id', $tenant->getKey());
                            $hasCachedGroups = $groupCacheQuery->exists();

                            $stalenessDays = (int) config('directory_groups.staleness_days', 30);
                            $cutoff = now('UTC')->subDays(max(1, $stalenessDays));
                            $latestSeen = $groupCacheQuery->max('last_seen_at');
                            $isStale = $hasCachedGroups && (! $latestSeen || $latestSeen < $cutoff);

                            $cacheNotice = match (true) {
                                ! $hasCachedGroups => 'No cached groups found. Run "Sync Groups" first.',
                                $isStale => "Cached groups may be stale (>{$stalenessDays} days). Consider running \"Sync Groups\".",
                                default => null,
                            };

                            return array_map(function (array $group) use ($cacheNotice): Forms\Components\TextInput {
                                $groupId = $group['id'];
                                $label = $group['label'];

                                return Forms\Components\TextInput::make("group_mapping.{$groupId}")
                                    ->label($label)
                                    ->placeholder('SKIP or target group Object ID (GUID)')
                                    ->rules([new SkipOrUuidRule])
                                    ->reactive()
                                    ->afterStateUpdated(function (Set $set, Get $get): void {
                                        $set('is_dry_run', true);
                                        $set('acknowledged_impact', false);
                                        $set('tenant_confirm', null);

                                        $draft = static::synchronizeRestoreSafetyDraft(static::draftDataSnapshot($get));

                                        $set('scope_basis', $draft['scope_basis']);
                                        $set('check_invalidation_reasons', $draft['check_invalidation_reasons']);
                                        $set('preview_invalidation_reasons', $draft['preview_invalidation_reasons']);
                                    })
                                    ->required()
                                    ->suffixAction(
                                        Actions\Action::make('select_from_directory_cache_'.str_replace('-', '_', $groupId))
                                            ->icon('heroicon-o-magnifying-glass')
                                            ->iconButton()
                                            ->tooltip('Select from Directory cache')
                                            ->modalHeading('Select from Directory cache')
                                            ->modalWidth('5xl')
                                            ->modalSubmitAction(false)
                                            ->modalCancelActionLabel('Close')
                                            ->modalContent(fn () => view('filament.modals.entra-group-cache-picker', [
                                                'sourceGroupId' => $groupId,
                                            ]))
                                    )
                                    ->helperText(fn (): string => ($cacheNotice ? ($cacheNotice.' ') : '').'Paste the target Entra ID group Object ID (GUID). Labels use cached directory groups only (no live Graph lookups). Use SKIP to omit the assignment.')
                                    ->hintAction(
                                        Actions\Action::make('open_directory_groups_'.str_replace('-', '_', $groupId))
                                            ->label('Sync Groups')
                                            ->icon('heroicon-o-arrow-path')
                                            ->color('warning')
                                            ->url(fn (): string => EntraGroupResource::getUrl('index', tenant: static::resolveTenantContextForCurrentPanel()))
                                            ->visible(fn (): bool => $cacheNotice !== null)
                                    );
                            }, $unresolved);
                        })
                        ->visible(function (Get $get): bool {
                            $backupSetId = $get('backup_set_id');
                            $scopeMode = $get('scope_mode') ?? 'all';
                            $selectedItemIds = $get('backup_item_ids');
                            $tenant = static::resolveTenantContextForCurrentPanel();

                            if (! $tenant || ! $backupSetId) {
                                return false;
                            }

                            $selectedItemIds = is_array($selectedItemIds) ? $selectedItemIds : null;

                            if ($scopeMode === 'selected' && ($selectedItemIds === null || $selectedItemIds === [])) {
                                return false;
                            }

                            return static::unresolvedGroups(
                                backupSetId: $backupSetId,
                                selectedItemIds: $scopeMode === 'selected' ? $selectedItemIds : null,
                                tenant: $tenant
                            ) !== [];
                        }),
                ]),
            Step::make('Safety & Conflict Checks')
                ->description('Is this dangerous?')
                ->schema([
                    Forms\Components\Hidden::make('scope_basis')
                        ->default(null),
                    Forms\Components\Hidden::make('check_summary')
                        ->default(null),
                    Forms\Components\Hidden::make('checks_ran_at')
                        ->default(null),
                    Forms\Components\Hidden::make('check_basis')
                        ->default(null),
                    Forms\Components\Hidden::make('check_invalidation_reasons')
                        ->default([]),
                    Forms\Components\ViewField::make('check_results')
                        ->label('Checks')
                        ->default([])
                        ->view('filament.forms.components.restore-run-checks')
                        ->viewData(fn (Get $get): array => [
                            'summary' => $get('check_summary'),
                            'ranAt' => $get('checks_ran_at'),
                            ...static::wizardSafetyState(static::draftDataSnapshot($get)),
                        ])
                        ->hintActions([
                            Actions\Action::make('run_restore_checks')
                                ->label('Run checks')
                                ->icon('heroicon-o-shield-check')
                                ->color('gray')
                                ->visible(fn (Get $get): bool => filled($get('backup_set_id')))
                                ->action(function (Get $get, Set $set): void {
                                    $tenant = static::resolveTenantContextForCurrentPanel();

                                    if (! $tenant) {
                                        return;
                                    }

                                    $backupSetId = $get('backup_set_id');

                                    if (! $backupSetId) {
                                        return;
                                    }

                                    $backupSet = BackupSet::find($backupSetId);

                                    if (! $backupSet || $backupSet->managed_environment_id !== $tenant->id) {
                                        Notification::make()
                                            ->title('Unable to run checks')
                                            ->body('Backup set is not available for the active tenant.')
                                            ->danger()
                                            ->send();

                                        return;
                                    }

                                    $scopeMode = $get('scope_mode') ?? 'all';
                                    $selectedItemIds = ($scopeMode === 'selected')
                                        ? ($get('backup_item_ids') ?? null)
                                        : null;

                                    $selectedItemIds = is_array($selectedItemIds) ? $selectedItemIds : null;

                                    $groupMapping = static::normalizeGroupMapping($get('group_mapping'));

                                    $checker = app(RestoreRiskChecker::class);
                                    $outcome = $checker->check(
                                        tenant: $tenant,
                                        backupSet: $backupSet,
                                        selectedItemIds: $selectedItemIds,
                                        groupMapping: $groupMapping,
                                    );

                                    $ranAt = now('UTC')->toIso8601String();
                                    $draft = [
                                        ...static::draftDataSnapshot($get),
                                        'check_summary' => $outcome['summary'] ?? [],
                                        'check_results' => $outcome['results'] ?? [],
                                        'checks_ran_at' => $ranAt,
                                    ];
                                    $draft['check_basis'] = static::restoreSafetyResolver()->checksBasisFromData($draft);
                                    $draft['check_invalidation_reasons'] = [];
                                    $draft = static::synchronizeRestoreSafetyDraft($draft);

                                    $set('check_summary', $draft['check_summary'], shouldCallUpdatedHooks: true);
                                    $set('check_results', $draft['check_results'], shouldCallUpdatedHooks: true);
                                    $set('checks_ran_at', $ranAt, shouldCallUpdatedHooks: true);
                                    $set('check_basis', $draft['check_basis'], shouldCallUpdatedHooks: true);
                                    $set('check_invalidation_reasons', [], shouldCallUpdatedHooks: true);
                                    $set('scope_basis', $draft['scope_basis'], shouldCallUpdatedHooks: true);

                                    $summary = $outcome['summary'] ?? [];
                                    $blockers = (int) ($summary['blocking'] ?? 0);
                                    $warnings = (int) ($summary['warning'] ?? 0);

                                    if ($blockers > 0) {
                                        $set('is_dry_run', true, shouldCallUpdatedHooks: true);
                                    }

                                    Notification::make()
                                        ->title('Safety checks completed')
                                        ->body("Blocking: {$blockers} • Warnings: {$warnings}")
                                        ->status($blockers > 0 ? 'danger' : ($warnings > 0 ? 'warning' : 'success'))
                                        ->send();
                                }),
                            Actions\Action::make('clear_restore_checks')
                                ->label('Clear')
                                ->icon('heroicon-o-x-mark')
                                ->color('gray')
                                ->visible(fn (Get $get): bool => filled($get('check_results')) || filled($get('check_summary')))
                                ->action(function (Set $set): void {
                                    $set('is_dry_run', true, shouldCallUpdatedHooks: true);
                                    $set('acknowledged_impact', false, shouldCallUpdatedHooks: true);
                                    $set('tenant_confirm', null, shouldCallUpdatedHooks: true);
                                    $set('check_summary', null, shouldCallUpdatedHooks: true);
                                    $set('check_results', [], shouldCallUpdatedHooks: true);
                                    $set('checks_ran_at', null, shouldCallUpdatedHooks: true);
                                    $set('check_basis', null, shouldCallUpdatedHooks: true);
                                    $set('check_invalidation_reasons', [], shouldCallUpdatedHooks: true);
                                }),
                        ])
                        ->helperText('Run checks after defining scope and mapping missing groups.'),
                ]),
            Step::make('Preview')
                ->description('Dry-run preview')
                ->schema([
                    Forms\Components\Hidden::make('preview_summary')
                        ->default(null),
                    Forms\Components\Hidden::make('preview_ran_at')
                        ->default(null)
                        ->required(),
                    Forms\Components\Hidden::make('preview_basis')
                        ->default(null),
                    Forms\Components\Hidden::make('preview_invalidation_reasons')
                        ->default([]),
                    Forms\Components\ViewField::make('preview_diffs')
                        ->label('Preview')
                        ->default([])
                        ->view('filament.forms.components.restore-run-preview')
                        ->viewData(fn (Get $get): array => [
                            'summary' => $get('preview_summary'),
                            'ranAt' => $get('preview_ran_at'),
                            ...static::wizardSafetyState(static::draftDataSnapshot($get)),
                        ])
                        ->hintActions([
                            Actions\Action::make('run_restore_preview')
                                ->label('Generate preview')
                                ->icon('heroicon-o-eye')
                                ->color('gray')
                                ->visible(fn (Get $get): bool => filled($get('backup_set_id')))
                                ->action(function (Get $get, Set $set): void {
                                    $tenant = static::resolveTenantContextForCurrentPanel();

                                    if (! $tenant) {
                                        return;
                                    }

                                    $backupSetId = $get('backup_set_id');

                                    if (! $backupSetId) {
                                        return;
                                    }

                                    $backupSet = BackupSet::find($backupSetId);

                                    if (! $backupSet || $backupSet->managed_environment_id !== $tenant->id) {
                                        Notification::make()
                                            ->title('Unable to generate preview')
                                            ->body('Backup set is not available for the active tenant.')
                                            ->danger()
                                            ->send();

                                        return;
                                    }

                                    $scopeMode = $get('scope_mode') ?? 'all';
                                    $selectedItemIds = ($scopeMode === 'selected')
                                        ? ($get('backup_item_ids') ?? null)
                                        : null;

                                    $selectedItemIds = is_array($selectedItemIds) ? $selectedItemIds : null;

                                    $generator = app(RestoreDiffGenerator::class);
                                    $outcome = $generator->generate(
                                        tenant: $tenant,
                                        backupSet: $backupSet,
                                        selectedItemIds: $selectedItemIds,
                                    );

                                    $summary = $outcome['summary'] ?? [];
                                    $diffs = $outcome['diffs'] ?? [];
                                    $ranAt = (string) ($summary['generated_at'] ?? now('UTC')->toIso8601String());
                                    $draft = [
                                        ...static::draftDataSnapshot($get),
                                        'preview_summary' => $summary,
                                        'preview_diffs' => $diffs,
                                        'preview_ran_at' => $ranAt,
                                    ];
                                    $draft['preview_basis'] = static::restoreSafetyResolver()->previewBasisFromData($draft);
                                    $draft['preview_invalidation_reasons'] = [];
                                    $draft = static::synchronizeRestoreSafetyDraft($draft);

                                    $set('preview_summary', $summary, shouldCallUpdatedHooks: true);
                                    $set('preview_diffs', $diffs, shouldCallUpdatedHooks: true);
                                    $set('preview_ran_at', $ranAt, shouldCallUpdatedHooks: true);
                                    $set('preview_basis', $draft['preview_basis'], shouldCallUpdatedHooks: true);
                                    $set('preview_invalidation_reasons', [], shouldCallUpdatedHooks: true);
                                    $set('scope_basis', $draft['scope_basis'], shouldCallUpdatedHooks: true);

                                    $policiesChanged = (int) ($summary['policies_changed'] ?? 0);
                                    $policiesTotal = (int) ($summary['policies_total'] ?? 0);

                                    Notification::make()
                                        ->title('Preview generated')
                                        ->body("Policies: {$policiesChanged}/{$policiesTotal} changed")
                                        ->status($policiesChanged > 0 ? 'warning' : 'success')
                                        ->send();
                                }),
                            Actions\Action::make('clear_restore_preview')
                                ->label('Clear')
                                ->icon('heroicon-o-x-mark')
                                ->color('gray')
                                ->visible(fn (Get $get): bool => filled($get('preview_diffs')) || filled($get('preview_summary')))
                                ->action(function (Set $set): void {
                                    $set('is_dry_run', true, shouldCallUpdatedHooks: true);
                                    $set('acknowledged_impact', false, shouldCallUpdatedHooks: true);
                                    $set('tenant_confirm', null, shouldCallUpdatedHooks: true);
                                    $set('preview_summary', null, shouldCallUpdatedHooks: true);
                                    $set('preview_diffs', [], shouldCallUpdatedHooks: true);
                                    $set('preview_ran_at', null, shouldCallUpdatedHooks: true);
                                    $set('preview_basis', null, shouldCallUpdatedHooks: true);
                                    $set('preview_invalidation_reasons', [], shouldCallUpdatedHooks: true);
                                }),
                        ])
                        ->helperText('Generate a normalized diff preview before creating the dry-run restore.'),
                ]),
            Step::make('Confirm & Execute')
                ->description('Point of no return')
                ->schema([
                    Forms\Components\Placeholder::make('confirm_environment')
                        ->label('Environment')
                        ->content(fn (): string => app()->environment('production') ? 'prod' : 'test'),
                    Forms\Components\Placeholder::make('confirm_tenant_label')
                        ->label('ManagedEnvironment hard-confirm label')
                        ->content(function (): string {
                            $tenant = static::resolveTenantContextForCurrentPanel();

                            if (! $tenant) {
                                return '';
                            }

                            $tenantIdentifier = $tenant->managed_environment_id ?? $tenant->external_id;

                            return (string) ($tenant->name ?? $tenantIdentifier ?? $tenant->getKey());
                        }),
                    Forms\Components\Placeholder::make('confirm_execution_readiness')
                        ->label('Technical startability')
                        ->content(function (Get $get): string {
                            $state = static::wizardSafetyState(static::draftDataSnapshot($get));
                            $readiness = $state['executionReadiness'];

                            if (! is_array($readiness)) {
                                return 'Execution readiness is unavailable.';
                            }

                            return (string) ($readiness['display_summary'] ?? 'Execution readiness is unavailable.');
                        }),
                    Forms\Components\Placeholder::make('confirm_safety_readiness')
                        ->label('Safety readiness')
                        ->content(function (Get $get): string {
                            $state = static::wizardSafetyState(static::draftDataSnapshot($get));
                            $assessment = $state['safetyAssessment'];

                            if (! is_array($assessment)) {
                                return 'Safety readiness is unavailable.';
                            }

                            return (string) ($assessment['summary'] ?? 'Safety readiness is unavailable.');
                        }),
                    Forms\Components\Placeholder::make('confirm_primary_next_step')
                        ->label('Primary next step')
                        ->content(function (Get $get): string {
                            $state = static::wizardSafetyState(static::draftDataSnapshot($get));
                            $assessment = $state['safetyAssessment'];

                            if (! is_array($assessment)) {
                                return 'Review the current scope and safety evidence.';
                            }

                            return RestoreSafetyCopy::primaryNextAction(
                                is_string($assessment['primary_next_action'] ?? null)
                                    ? $assessment['primary_next_action']
                                    : 'review_scope'
                            );
                        }),
                    Forms\Components\Toggle::make('is_dry_run')
                        ->label('Preview only (dry-run)')
                        ->default(true)
                        ->reactive()
                        ->disabled(function (Get $get): bool {
                            $state = static::wizardSafetyState(static::draftDataSnapshot($get));
                            $readiness = $state['executionReadiness'];

                            return ! is_array($readiness) || ! (bool) ($readiness['allowed'] ?? false);
                        })
                        ->helperText(function (Get $get): string {
                            $state = static::wizardSafetyState(static::draftDataSnapshot($get));
                            $assessment = $state['safetyAssessment'];

                            if (! is_array($assessment)) {
                                return 'Turn OFF to queue a real execution. Execution requires checks, preview, and confirmations.';
                            }

                            return (string) ($assessment['summary'] ?? 'Turn OFF to queue a real execution. Execution requires checks, preview, and confirmations.');
                        }),
                    Forms\Components\Checkbox::make('acknowledged_impact')
                        ->label('I reviewed the impact (checks + preview)')
                        ->accepted()
                        ->visible(fn (Get $get): bool => $get('is_dry_run') === false),
                    Forms\Components\TextInput::make('tenant_confirm')
                        ->label('Type the tenant label to confirm execution')
                        ->required(fn (Get $get): bool => $get('is_dry_run') === false)
                        ->visible(fn (Get $get): bool => $get('is_dry_run') === false)
                        ->in(function (): array {
                            $tenant = static::resolveTenantContextForCurrentPanel();

                            if (! $tenant) {
                                return [];
                            }

                            $tenantIdentifier = $tenant->managed_environment_id ?? $tenant->external_id;

                            return [(string) ($tenant->name ?? $tenantIdentifier ?? $tenant->getKey())];
                        })
                        ->validationMessages([
                            'in' => 'ManagedEnvironment hard-confirm does not match.',
                        ])
                        ->helperText(function (): string {
                            $tenant = static::resolveTenantContextForCurrentPanel();

                            if (! $tenant) {
                                return '';
                            }

                            $tenantIdentifier = $tenant->managed_environment_id ?? $tenant->external_id;
                            $expected = (string) ($tenant->name ?? $tenantIdentifier ?? $tenant->getKey());

                            return "Type: {$expected}";
                        }),
                ]),
        ];
    }

    public static function table(Table $table): Table
    {
        return $table
            ->persistFiltersInSession()
            ->persistSearchInSession()
            ->persistSortInSession()
            ->columns([
                Tables\Columns\TextColumn::make('backupSet.name')
                    ->label('Backup set')
                    ->searchable()
                    ->sortable(),
                Tables\Columns\IconColumn::make('is_dry_run')->label('Dry-run')->boolean(),
                Tables\Columns\TextColumn::make('status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::RestoreRunStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::RestoreRunStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::RestoreRunStatus))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::RestoreRunStatus))
                    ->sortable(),
                Tables\Columns\TextColumn::make('result_attention_summary')
                    ->label('Result attention')
                    ->state(fn (RestoreRun $record): string => static::restoreSafetyResolver()->resultAttentionForRun($record)->summary)
                    ->wrap(),
                Tables\Columns\TextColumn::make('summary_total')
                    ->label('Total')
                    ->state(fn (RestoreRun $record): int => (int) (($record->metadata ?? [])['total'] ?? 0)),
                Tables\Columns\TextColumn::make('summary_succeeded')
                    ->label('Applied')
                    ->state(fn (RestoreRun $record): int => (int) (($record->metadata ?? [])['succeeded'] ?? 0)),
                Tables\Columns\TextColumn::make('summary_failed')
                    ->label('Failed items')
                    ->state(fn (RestoreRun $record): int => (int) (($record->metadata ?? [])['failed'] ?? 0)),
                Tables\Columns\TextColumn::make('started_at')->dateTime()->since()->sortable(),
                Tables\Columns\TextColumn::make('completed_at')->dateTime()->since()->sortable(),
                Tables\Columns\TextColumn::make('requested_by')->label('Requested by')->searchable()->sortable(),
            ])
            ->filters([
                Tables\Filters\SelectFilter::make('status')
                    ->options(FilterOptionCatalog::restoreRunStatuses())
                    ->searchable(),
                Tables\Filters\SelectFilter::make('outcome')
                    ->options(FilterOptionCatalog::restoreRunOutcomes())
                    ->query(function (\Illuminate\Database\Eloquent\Builder $query, array $data): \Illuminate\Database\Eloquent\Builder {
                        $value = $data['value'] ?? null;

                        return match ((string) $value) {
                            'succeeded' => $query->whereIn('status', [
                                \App\Support\RestoreRunStatus::Previewed->value,
                                \App\Support\RestoreRunStatus::Completed->value,
                            ]),
                            'partial' => $query->whereIn('status', [
                                \App\Support\RestoreRunStatus::Partial->value,
                                \App\Support\RestoreRunStatus::CompletedWithErrors->value,
                            ]),
                            'failed' => $query->whereIn('status', [
                                \App\Support\RestoreRunStatus::Failed->value,
                                \App\Support\RestoreRunStatus::Cancelled->value,
                                \App\Support\RestoreRunStatus::Aborted->value,
                            ]),
                            default => $query,
                        };
                    }),
                FilterPresets::dateRange('started_at', 'Started', 'started_at'),
                FilterPresets::archived(),
            ])
            ->recordUrl(fn (RestoreRun $record): string => static::getUrl('view', ['record' => $record]))
            ->actions([
                ActionGroup::make([
                    static::rerunActionWithGate(),
                    UiEnforcement::forTableAction(
                        Actions\Action::make('restore')
                            ->label('Restore')
                            ->color('success')
                            ->icon('heroicon-o-arrow-uturn-left')
                            ->requiresConfirmation()
                            ->visible(fn (RestoreRun $record): bool => $record->trashed())
                            ->action(function (RestoreRun $record, \App\Services\Intune\AuditLogger $auditLogger) {
                                $record = static::resolveProtectedRestoreRunRecordOrFail($record);

                                $record->restore();

                                if ($record->tenant) {
                                    $auditLogger->log(
                                        tenant: $record->tenant,
                                        action: 'restore_run.restored',
                                        resourceType: 'restore_run',
                                        resourceId: (string) $record->id,
                                        status: 'success',
                                        context: ['metadata' => ['backup_set_id' => $record->backup_set_id]]
                                    );
                                }

                                Notification::make()
                                    ->title('Restore run restored')
                                    ->success()
                                    ->send();
                            }),
                        fn () => static::resolveTenantContextForCurrentPanel(),
                    )
                        ->requireCapability(Capabilities::TENANT_MANAGE)
                        ->preserveVisibility()
                        ->apply(),
                    UiEnforcement::forTableAction(
                        Actions\Action::make('archive')
                            ->label('Archive')
                            ->color('danger')
                            ->icon('heroicon-o-archive-box-x-mark')
                            ->requiresConfirmation()
                            ->visible(fn (RestoreRun $record): bool => ! $record->trashed())
                            ->action(function (RestoreRun $record, \App\Services\Intune\AuditLogger $auditLogger) {
                                $record = static::resolveProtectedRestoreRunRecordOrFail($record);

                                if (! $record->isDeletable()) {
                                    Notification::make()
                                        ->title('Restore run cannot be archived')
                                        ->body("Not deletable (status: {$record->status})")
                                        ->warning()
                                        ->send();

                                    return;
                                }

                                $record->delete();

                                if ($record->tenant) {
                                    $auditLogger->log(
                                        tenant: $record->tenant,
                                        action: 'restore_run.deleted',
                                        resourceType: 'restore_run',
                                        resourceId: (string) $record->id,
                                        status: 'success',
                                        context: ['metadata' => ['backup_set_id' => $record->backup_set_id]]
                                    );
                                }

                                Notification::make()
                                    ->title('Restore run archived')
                                    ->success()
                                    ->send();
                            }),
                        fn () => static::resolveTenantContextForCurrentPanel(),
                    )
                        ->requireCapability(Capabilities::TENANT_MANAGE)
                        ->preserveVisibility()
                        ->apply(),
                    UiEnforcement::forTableAction(
                        Actions\Action::make('forceDelete')
                            ->label('Force delete')
                            ->color('danger')
                            ->icon('heroicon-o-trash')
                            ->requiresConfirmation()
                            ->visible(fn (RestoreRun $record): bool => $record->trashed())
                            ->action(function (RestoreRun $record, \App\Services\Intune\AuditLogger $auditLogger) {
                                $record = static::resolveProtectedRestoreRunRecordOrFail($record);

                                if ($record->tenant) {
                                    $auditLogger->log(
                                        tenant: $record->tenant,
                                        action: 'restore_run.force_deleted',
                                        resourceType: 'restore_run',
                                        resourceId: (string) $record->id,
                                        status: 'success',
                                        context: ['metadata' => ['backup_set_id' => $record->backup_set_id]]
                                    );
                                }

                                $record->forceDelete();

                                Notification::make()
                                    ->title('Restore run permanently deleted')
                                    ->success()
                                    ->send();
                            }),
                        fn () => static::resolveTenantContextForCurrentPanel(),
                    )
                        ->requireCapability(Capabilities::TENANT_DELETE)
                        ->preserveVisibility()
                        ->apply(),
                ])
                    ->label('More')
                    ->icon('heroicon-o-ellipsis-vertical'),
            ])
            ->bulkActions([
                BulkActionGroup::make([
                    UiEnforcement::forBulkAction(
                        BulkAction::make('bulk_delete')
                            ->label('Archive Restore Runs')
                            ->icon('heroicon-o-trash')
                            ->color('danger')
                            ->requiresConfirmation()
                            ->hidden(function (HasTable $livewire): bool {
                                $trashedFilterState = $livewire->getTableFilterState(TrashedFilter::class) ?? [];
                                $value = $trashedFilterState['value'] ?? null;

                                $isOnlyTrashed = in_array($value, [0, '0', false], true);

                                return $isOnlyTrashed;
                            })
                            ->modalDescription('This archives restore runs (soft delete). Already archived runs will be skipped.')
                            ->form(function (Collection $records) {
                                if ($records->count() >= 20) {
                                    return [
                                        Forms\Components\TextInput::make('confirmation')
                                            ->label('Type DELETE to confirm')
                                            ->in(['DELETE'])
                                            ->validationMessages([
                                                'in' => 'Please type DELETE to confirm.',
                                            ]),
                                    ];
                                }

                                return [];
                            })
                            ->action(function (Collection $records) {
                                $tenant = static::resolveTenantContextForCurrentPanel();
                                $user = auth()->user();
                                $count = $records->count();
                                $ids = static::resolveProtectedRestoreRunIds($records);

                                if (! $tenant instanceof ManagedEnvironment) {
                                    return;
                                }

                                $initiator = $user instanceof User ? $user : null;

                                /** @var BulkSelectionIdentity $selection */
                                $selection = app(BulkSelectionIdentity::class);
                                $selectionIdentity = $selection->fromIds($ids);

                                /** @var OperationRunService $runs */
                                $runs = app(OperationRunService::class);

                                $opRun = $runs->enqueueBulkOperation(
                                    tenant: $tenant,
                                    type: 'restore_run.delete',
                                    targetScope: [
                                        'entra_tenant_id' => (string) ($tenant->managed_environment_id ?? $tenant->external_id),
                                    ],
                                    selectionIdentity: $selectionIdentity,
                                    dispatcher: function ($operationRun) use ($tenant, $initiator, $ids): void {
                                        BulkRestoreRunDeleteJob::dispatch(
                                            tenantId: (int) $tenant->getKey(),
                                            userId: (int) ($initiator?->getKey() ?? 0),
                                            restoreRunIds: $ids,
                                            operationRun: $operationRun,
                                        );
                                    },
                                    initiator: $initiator,
                                    extraContext: [
                                        'restore_run_count' => $count,
                                    ],
                                    emitQueuedNotification: false,
                                );

                                OperationUxPresenter::queuedToast('restore_run.delete')
                                    ->actions([
                                        Actions\Action::make('view_run')
                                            ->label('Open operation')
                                            ->url(OperationRunLinks::view($opRun, $tenant)),
                                    ])
                                    ->send();
                            })
                            ->deselectRecordsAfterCompletion(),
                    )
                        ->requireCapability(Capabilities::TENANT_MANAGE)
                        ->apply(),

                    UiEnforcement::forBulkAction(
                        BulkAction::make('bulk_restore')
                            ->label('Restore Restore Runs')
                            ->icon('heroicon-o-arrow-uturn-left')
                            ->color('success')
                            ->requiresConfirmation()
                            ->hidden(function (HasTable $livewire): bool {
                                $trashedFilterState = $livewire->getTableFilterState(TrashedFilter::class) ?? [];
                                $value = $trashedFilterState['value'] ?? null;

                                $isOnlyTrashed = in_array($value, [0, '0', false], true);

                                return ! $isOnlyTrashed;
                            })
                            ->modalHeading(fn (Collection $records) => "Restore {$records->count()} restore runs?")
                            ->modalDescription('Archived runs will be restored back to the active list. Active runs will be skipped.')
                            ->action(function (Collection $records) {
                                $tenant = static::resolveTenantContextForCurrentPanel();
                                $user = auth()->user();
                                $count = $records->count();
                                $ids = static::resolveProtectedRestoreRunIds($records);

                                if (! $tenant instanceof ManagedEnvironment) {
                                    return;
                                }

                                $initiator = $user instanceof User ? $user : null;

                                /** @var BulkSelectionIdentity $selection */
                                $selection = app(BulkSelectionIdentity::class);
                                $selectionIdentity = $selection->fromIds($ids);

                                /** @var OperationRunService $runs */
                                $runs = app(OperationRunService::class);

                                $opRun = $runs->enqueueBulkOperation(
                                    tenant: $tenant,
                                    type: 'restore_run.restore',
                                    targetScope: [
                                        'entra_tenant_id' => (string) ($tenant->managed_environment_id ?? $tenant->external_id),
                                    ],
                                    selectionIdentity: $selectionIdentity,
                                    dispatcher: function ($operationRun) use ($count, $tenant, $initiator, $ids): void {
                                        if ($count >= 20) {
                                            BulkRestoreRunRestoreJob::dispatch(
                                                tenantId: (int) $tenant->getKey(),
                                                userId: (int) ($initiator?->getKey() ?? 0),
                                                restoreRunIds: $ids,
                                                operationRun: $operationRun,
                                            );

                                            return;
                                        }

                                        BulkRestoreRunRestoreJob::dispatchSync(
                                            tenantId: (int) $tenant->getKey(),
                                            userId: (int) ($initiator?->getKey() ?? 0),
                                            restoreRunIds: $ids,
                                            operationRun: $operationRun,
                                        );
                                    },
                                    initiator: $initiator,
                                    extraContext: [
                                        'restore_run_count' => $count,
                                    ],
                                    emitQueuedNotification: false,
                                );

                                OperationUxPresenter::queuedToast('restore_run.restore')
                                    ->actions([
                                        Actions\Action::make('view_run')
                                            ->label('Open operation')
                                            ->url(OperationRunLinks::view($opRun, $tenant)),
                                    ])
                                    ->send();
                            })
                            ->deselectRecordsAfterCompletion(),
                    )
                        ->requireCapability(Capabilities::TENANT_MANAGE)
                        ->apply(),

                    UiEnforcement::forBulkAction(
                        BulkAction::make('bulk_force_delete')
                            ->label('Force Delete Restore Runs')
                            ->icon('heroicon-o-trash')
                            ->color('danger')
                            ->requiresConfirmation()
                            ->hidden(function (HasTable $livewire): bool {
                                $trashedFilterState = $livewire->getTableFilterState(TrashedFilter::class) ?? [];
                                $value = $trashedFilterState['value'] ?? null;

                                $isOnlyTrashed = in_array($value, [0, '0', false], true);

                                return ! $isOnlyTrashed;
                            })
                            ->modalHeading(fn (Collection $records) => "Force delete {$records->count()} restore runs?")
                            ->modalDescription('This is permanent. Only archived restore runs will be permanently deleted; active runs will be skipped.')
                            ->form([
                                Forms\Components\TextInput::make('confirmation')
                                    ->label('Type DELETE to confirm')
                                    ->required()
                                    ->in(['DELETE'])
                                    ->validationMessages([
                                        'in' => 'Please type DELETE to confirm.',
                                    ]),
                            ])
                            ->action(function (Collection $records) {
                                $tenant = static::resolveTenantContextForCurrentPanel();
                                $user = auth()->user();
                                $count = $records->count();
                                $ids = static::resolveProtectedRestoreRunIds($records);

                                if (! $tenant instanceof ManagedEnvironment) {
                                    return;
                                }

                                $initiator = $user instanceof User ? $user : null;

                                /** @var BulkSelectionIdentity $selection */
                                $selection = app(BulkSelectionIdentity::class);
                                $selectionIdentity = $selection->fromIds($ids);

                                /** @var OperationRunService $runs */
                                $runs = app(OperationRunService::class);

                                $opRun = $runs->enqueueBulkOperation(
                                    tenant: $tenant,
                                    type: 'restore_run.force_delete',
                                    targetScope: [
                                        'entra_tenant_id' => (string) ($tenant->managed_environment_id ?? $tenant->external_id),
                                    ],
                                    selectionIdentity: $selectionIdentity,
                                    dispatcher: function ($operationRun) use ($count, $tenant, $initiator, $ids): void {
                                        if ($count >= 20) {
                                            BulkRestoreRunForceDeleteJob::dispatch(
                                                tenantId: (int) $tenant->getKey(),
                                                userId: (int) ($initiator?->getKey() ?? 0),
                                                restoreRunIds: $ids,
                                                operationRun: $operationRun,
                                            );

                                            return;
                                        }

                                        BulkRestoreRunForceDeleteJob::dispatchSync(
                                            tenantId: (int) $tenant->getKey(),
                                            userId: (int) ($initiator?->getKey() ?? 0),
                                            restoreRunIds: $ids,
                                            operationRun: $operationRun,
                                        );
                                    },
                                    initiator: $initiator,
                                    extraContext: [
                                        'restore_run_count' => $count,
                                    ],
                                    emitQueuedNotification: false,
                                );

                                OperationUxPresenter::queuedToast('restore_run.force_delete')
                                    ->actions([
                                        Actions\Action::make('view_run')
                                            ->label('Open operation')
                                            ->url(OperationRunLinks::view($opRun, $tenant)),
                                    ])
                                    ->send();
                            })
                            ->deselectRecordsAfterCompletion(),
                    )
                        ->requireCapability(Capabilities::TENANT_DELETE)
                        ->apply(),
                ])->label('More'),
            ])
            ->emptyStateHeading('No restore runs')
            ->emptyStateDescription('Start a restoration from a backup set.')
            ->emptyStateIcon('heroicon-o-arrow-path-rounded-square')
            ->emptyStateActions([
                static::makeCreateAction(),
            ]);
    }

    public static function infolist(Schema $schema): Schema
    {
        return $schema
            ->schema([
                Infolists\Components\TextEntry::make('backupSet.name')->label('Backup set'),
                Infolists\Components\TextEntry::make('status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::RestoreRunStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::RestoreRunStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::RestoreRunStatus))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::RestoreRunStatus)),
                Infolists\Components\TextEntry::make('counts')
                    ->label('Counts')
                    ->state(function (RestoreRun $record): string {
                        $meta = $record->metadata ?? [];
                        $total = (int) ($meta['total'] ?? 0);
                        $succeeded = (int) ($meta['succeeded'] ?? 0);
                        $failed = (int) ($meta['failed'] ?? 0);

                        return sprintf('Total: %d • Applied: %d • Failed items: %d', $total, $succeeded, $failed);
                    }),
                Infolists\Components\TextEntry::make('is_dry_run')
                    ->label('Dry-run')
                    ->formatStateUsing(fn ($state) => $state ? 'Yes' : 'No')
                    ->badge(),
                Infolists\Components\TextEntry::make('requested_by'),
                Infolists\Components\TextEntry::make('started_at')->dateTime(),
                Infolists\Components\TextEntry::make('completed_at')->dateTime(),
                Infolists\Components\ViewEntry::make('preview')
                    ->label('Preview')
                    ->view('filament.infolists.entries.restore-preview')
                    ->state(fn (RestoreRun $record): array => static::detailPreviewState($record)),
                Infolists\Components\ViewEntry::make('results')
                    ->label('Results')
                    ->view('filament.infolists.entries.restore-results')
                    ->state(fn (RestoreRun $record): array => static::detailResultsState($record)),
            ]);
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListRestoreRuns::route('/'),
            'create' => Pages\CreateRestoreRun::route('/create'),
            'view' => Pages\ViewRestoreRun::route('/{record}'),
        ];
    }

    /**
     * @return array{label:?string,category:?string,restore:?string,risk:?string}|array<string,mixed>
     */
    private static function typeMeta(?string $type): array
    {
        if ($type === null) {
            return [];
        }

        $types = array_merge(
            config('tenantpilot.supported_policy_types', []),
            config('tenantpilot.foundation_types', [])
        );

        return collect($types)
            ->firstWhere('type', $type) ?? [];
    }

    /**
     * @return array{options: array<int, string>, descriptions: array<int, string>}
     */
    private static function restoreItemOptionData(?int $backupSetId): array
    {
        $tenant = static::resolveTenantContextForCurrentPanel();

        if (! $tenant || ! $backupSetId) {
            return [
                'options' => [],
                'descriptions' => [],
            ];
        }

        $cacheKey = sprintf('restore_run_item_options:%s:%s', $tenant->getKey(), $backupSetId);

        return Cache::store('array')->rememberForever($cacheKey, function () use ($backupSetId, $tenant): array {
            $items = BackupItem::query()
                ->where('backup_set_id', $backupSetId)
                ->whereHas('backupSet', fn ($query) => $query->where('managed_environment_id', $tenant->getKey()))
                ->where(function ($query) {
                    $query->whereNull('policy_id')
                        ->orWhereDoesntHave('policy')
                        ->orWhereHas('policy', function ($policyQuery): void {
                            $policyQuery
                                ->whereNull('ignored_at')
                                ->orWhereNotNull('missing_from_provider_at');
                        });
                })
                ->with(['policy:id,display_name,missing_from_provider_at,ignored_at', 'policyVersion:id,version_number,captured_at'])
                ->get()
                ->sortBy(function (BackupItem $item) {
                    $meta = static::typeMeta($item->policy_type);
                    $category = $meta['category'] ?? 'Policies';
                    $categoryKey = $category === 'Foundations' ? 'zz-'.$category : $category;
                    $name = strtolower($item->resolvedDisplayName());

                    return strtolower($categoryKey.'-'.$name);
                });

            $options = [];
            $descriptions = [];

            foreach ($items as $item) {
                $meta = static::typeMeta($item->policy_type);
                $qualitySummary = static::backupItemQualitySummary($item);
                $typeLabel = $meta['label'] ?? $item->policy_type;
                $category = $meta['category'] ?? 'Policies';
                $restore = $meta['restore'] ?? 'enabled';
                $platform = $item->platform ?? $meta['platform'] ?? null;
                $displayName = $item->resolvedDisplayName();
                $identifier = $item->policy_identifier ?? null;
                $versionNumber = $item->policyVersion?->version_number;
                $providerMissingNote = $item->policy?->missing_from_provider_at
                    ? 'current state: provider missing; historical restore available'
                    : null;

                $options[$item->id] = $displayName;

                $parts = array_filter([
                    $category,
                    $typeLabel,
                    $platform,
                    'quality: '.$qualitySummary->compactSummary,
                    "restore: {$restore}",
                    $providerMissingNote,
                    $versionNumber ? "version: {$versionNumber}" : null,
                    $item->hasAssignments() ? "assignments: {$item->assignment_count}" : null,
                    $identifier ? 'id: '.Str::limit($identifier, 24, '...') : null,
                ]);

                $descriptions[$item->id] = implode(' • ', $parts);
            }

            return [
                'options' => $options,
                'descriptions' => $descriptions,
            ];
        });
    }

    /**
     * @return array<string, array<int|string, string>>
     */
    private static function restoreItemGroupedOptions(?int $backupSetId): array
    {
        $tenant = static::resolveTenantContextForCurrentPanel();

        if (! $tenant || ! $backupSetId) {
            return [];
        }

        $items = BackupItem::query()
            ->where('backup_set_id', $backupSetId)
            ->whereHas('backupSet', fn ($query) => $query->where('managed_environment_id', $tenant->getKey()))
            ->where(function ($query) {
                $query->whereNull('policy_id')
                    ->orWhereDoesntHave('policy')
                    ->orWhereHas('policy', function ($policyQuery): void {
                        $policyQuery
                            ->whereNull('ignored_at')
                            ->orWhereNotNull('missing_from_provider_at');
                    });
            })
            ->with(['policy:id,display_name,missing_from_provider_at,ignored_at'])
            ->get()
            ->sortBy(function (BackupItem $item) {
                $meta = static::typeMeta($item->policy_type);
                $category = $meta['category'] ?? 'Policies';
                $categoryKey = $category === 'Foundations' ? 'zz-'.$category : $category;
                $typeLabel = $meta['label'] ?? $item->policy_type;
                $platform = $item->platform ?? $meta['platform'] ?? null;
                $name = strtolower($item->resolvedDisplayName());

                return strtolower($categoryKey.'-'.$typeLabel.'-'.$platform.'-'.$name);
            });

        $groups = [];

        foreach ($items as $item) {
            $meta = static::typeMeta($item->policy_type);
            $typeLabel = $meta['label'] ?? $item->policy_type;
            $category = $meta['category'] ?? 'Policies';
            $platform = $item->platform ?? $meta['platform'] ?? 'all';
            $restoreMode = $meta['restore'] ?? 'enabled';

            $groupLabel = implode(' • ', array_filter([
                $category,
                $typeLabel,
                $platform,
                $restoreMode === 'preview-only' ? 'preview-only' : null,
            ]));

            $groups[$groupLabel] ??= [];
            $groups[$groupLabel][$item->id] = static::restoreItemSelectionLabel($item);
        }

        return $groups;
    }

    /**
     * @return array<int, string>
     */
    private static function restoreBackupSetOptions(): array
    {
        $tenantId = static::resolveTenantContextForCurrentPanelOrFail()->getKey();

        return BackupSet::query()
            ->where('managed_environment_id', $tenantId)
            ->with([
                'items' => fn ($query) => $query->select([
                    'id',
                    'backup_set_id',
                    'payload',
                    'metadata',
                    'assignments',
                ]),
            ])
            ->orderByDesc('created_at')
            ->get()
            ->mapWithKeys(fn (BackupSet $set): array => [
                (int) $set->getKey() => static::restoreBackupSetSelectionLabel($set),
            ])
            ->all();
    }

    private static function restoreBackupSetSelectionLabel(BackupSet $set): string
    {
        $qualitySummary = static::backupSetQualitySummary($set);

        return implode(' • ', array_filter([
            $set->name,
            sprintf('%d items', (int) ($set->item_count ?? 0)),
            optional($set->created_at)->format('Y-m-d H:i'),
            $qualitySummary->compactSummary,
        ]));
    }

    private static function restoreBackupSetHelperText(mixed $backupSetId): string
    {
        $default = 'Backup quality hints describe input strength only. They do not approve restore execution or prove recoverability.';

        if (! is_numeric($backupSetId)) {
            return $default;
        }

        $tenant = static::resolveTenantContextForCurrentPanel();

        if (! $tenant instanceof ManagedEnvironment) {
            return $default;
        }

        $backupSet = BackupSet::query()
            ->where('managed_environment_id', (int) $tenant->getKey())
            ->with([
                'items' => fn ($query) => $query->select([
                    'id',
                    'backup_set_id',
                    'payload',
                    'metadata',
                    'assignments',
                ]),
            ])
            ->find((int) $backupSetId);

        if (! $backupSet instanceof BackupSet) {
            return $default;
        }

        $summary = static::backupSetQualitySummary($backupSet);

        return $summary->compactSummary.'. '.$summary->positiveClaimBoundary;
    }

    private static function restoreItemSelectionLabel(BackupItem $item): string
    {
        $summary = static::backupItemQualitySummary($item);

        return implode(' • ', array_filter([
            $item->resolvedDisplayName(),
            $summary->compactSummary,
            $item->policy?->missing_from_provider_at ? 'provider missing now' : null,
        ]));
    }

    private static function restoreItemQualityHelperText(): string
    {
        return 'Quality hints describe input strength before risk checks. Include foundations with policies when you need ID re-mapping context.';
    }

    private static function backupSetQualitySummary(BackupSet $backupSet): \App\Support\BackupQuality\BackupQualitySummary
    {
        return app(BackupQualityResolver::class)->summarizeBackupSet($backupSet);
    }

    private static function backupItemQualitySummary(BackupItem $backupItem): \App\Support\BackupQuality\BackupQualitySummary
    {
        return app(BackupQualityResolver::class)->forBackupItem($backupItem);
    }

    public static function createRestoreRun(array $data): RestoreRun
    {
        $tenant = static::resolveTenantContextForCurrentPanel();
        $user = auth()->user();

        if (! $tenant instanceof ManagedEnvironment || ! $user instanceof User) {
            abort(403);
        }

        /** @var CapabilityResolver $resolver */
        $resolver = app(CapabilityResolver::class);

        if (! $resolver->isMember($user, $tenant)) {
            abort(404);
        }

        if (! $resolver->can($user, $tenant, Capabilities::TENANT_MANAGE)) {
            abort(403);
        }

        /** @var BackupSet $backupSet */
        $backupSet = BackupSet::findOrFail($data['backup_set_id']);

        if ($backupSet->managed_environment_id !== $tenant->id) {
            abort(403, 'Backup set does not belong to the active tenant.');
        }

        /** @var RestoreService $service */
        $service = app(RestoreService::class);

        $scopeMode = $data['scope_mode'] ?? 'all';
        $selectedItemIds = ($scopeMode === 'selected') ? ($data['backup_item_ids'] ?? null) : null;
        $selectedItemIds = is_array($selectedItemIds) ? $selectedItemIds : null;

        $actorEmail = auth()->user()?->email;
        $actorName = auth()->user()?->name;
        $isDryRun = (bool) ($data['is_dry_run'] ?? true);
        $groupMapping = static::normalizeGroupMapping($data['group_mapping'] ?? null);
        $data = static::synchronizeRestoreSafetyDraft([
            ...$data,
            'group_mapping' => $groupMapping,
        ]);
        $restoreSafetyResolver = static::restoreSafetyResolver();
        $scopeBasis = is_array($data['scope_basis'] ?? null)
            ? $data['scope_basis']
            : $restoreSafetyResolver->scopeBasisFromData($data);
        $checkBasis = is_array($data['check_basis'] ?? null)
            ? $data['check_basis']
            : $restoreSafetyResolver->checksBasisFromData($data);
        $previewBasis = is_array($data['preview_basis'] ?? null)
            ? $data['preview_basis']
            : $restoreSafetyResolver->previewBasisFromData($data);
        $data = [
            ...$data,
            'scope_basis' => $scopeBasis,
            'check_basis' => $checkBasis,
            'preview_basis' => $previewBasis,
        ];

        $checkSummary = $data['check_summary'] ?? null;
        $checkResults = $data['check_results'] ?? null;
        $checksRanAt = $data['checks_ran_at'] ?? null;
        $previewSummary = $data['preview_summary'] ?? null;
        $previewDiffs = $data['preview_diffs'] ?? null;
        $previewRanAt = $data['preview_ran_at'] ?? null;

        $tenantIdentifier = $tenant->managed_environment_id ?? $tenant->external_id;
        $highlanderLabel = (string) ($tenant->name ?? $tenantIdentifier ?? $tenant->getKey());

        if (! $isDryRun) {
            try {
                app(WriteGateInterface::class)->evaluate($tenant, 'restore.execute');
            } catch (ProviderAccessHardeningRequired $e) {
                app(\App\Services\Intune\AuditLogger::class)->log(
                    tenant: $tenant,
                    action: 'intune_rbac.write_blocked',
                    status: 'blocked',
                    actorId: (int) $user->getKey(),
                    actorEmail: $user->email,
                    actorName: $user->name,
                    resourceType: 'restore_run',
                    context: [
                        'metadata' => [
                            'operation_type' => 'restore.execute',
                            'reason_code' => $e->reasonCode,
                            'backup_set_id' => $data['backup_set_id'] ?? null,
                        ],
                    ],
                );

                Notification::make()
                    ->title('Write operation blocked')
                    ->body($e->reasonMessage)
                    ->danger()
                    ->send();

                throw ValidationException::withMessages([
                    'backup_set_id' => $e->reasonMessage,
                ]);
            }

            $previewIntegrity = $restoreSafetyResolver->previewIntegrityFromData($data);
            $checksIntegrity = $restoreSafetyResolver->checksIntegrityFromData($data);
            $assessment = $restoreSafetyResolver->safetyAssessment($tenant, $user, $data);

            if ($checksIntegrity->state === ChecksIntegrityState::STATE_NOT_RUN) {
                throw ValidationException::withMessages([
                    'check_summary' => 'Run safety checks before executing.',
                ]);
            }

            if ($checksIntegrity->state !== ChecksIntegrityState::STATE_CURRENT) {
                throw ValidationException::withMessages([
                    'check_summary' => 'Run safety checks again for the current scope before executing.',
                ]);
            }

            if ($checksIntegrity->blockingCount > 0 || $assessment->state === 'blocked') {
                throw ValidationException::withMessages([
                    'check_summary' => 'Blocking checks must be resolved before executing.',
                ]);
            }

            if ($previewIntegrity->state === PreviewIntegrityState::STATE_NOT_GENERATED) {
                throw ValidationException::withMessages([
                    'preview_ran_at' => 'Generate preview before executing.',
                ]);
            }

            if ($previewIntegrity->state !== PreviewIntegrityState::STATE_CURRENT) {
                throw ValidationException::withMessages([
                    'preview_ran_at' => 'Generate preview again for the current scope before executing.',
                ]);
            }

            if (! (bool) ($data['acknowledged_impact'] ?? false)) {
                throw ValidationException::withMessages([
                    'acknowledged_impact' => 'Please acknowledge that you reviewed the impact.',
                ]);
            }

            $tenantConfirm = $data['tenant_confirm'] ?? null;

            if (! is_string($tenantConfirm) || $tenantConfirm !== $highlanderLabel) {
                throw ValidationException::withMessages([
                    'tenant_confirm' => 'ManagedEnvironment hard-confirm does not match.',
                ]);
            }
        }

        if ($isDryRun) {
            $restoreRun = $service->execute(
                tenant: $tenant,
                backupSet: $backupSet,
                selectedItemIds: $selectedItemIds,
                dryRun: true,
                actorEmail: $actorEmail,
                actorName: $actorName,
                groupMapping: $groupMapping,
            );

            $metadata = $restoreRun->metadata ?? [];

            if (is_array($checkSummary)) {
                $metadata['check_summary'] = $checkSummary;
            }

            if (is_array($checkResults)) {
                $metadata['check_results'] = $checkResults;
            }

            if (is_string($checksRanAt) && $checksRanAt !== '') {
                $metadata['checks_ran_at'] = $checksRanAt;
            }

            if (is_array($previewSummary)) {
                $metadata['preview_summary'] = $previewSummary;
            }

            if (is_array($previewDiffs)) {
                $metadata['preview_diffs'] = $previewDiffs;
            }

            if (is_string($previewRanAt) && $previewRanAt !== '') {
                $metadata['preview_ran_at'] = $previewRanAt;
            }

            $metadata['scope_basis'] = $scopeBasis;

            if (is_array($checkBasis)) {
                $metadata['check_basis'] = $checkBasis;
            }

            if (is_array($previewBasis)) {
                $metadata['preview_basis'] = $previewBasis;
            }

            $restoreRun->update(['metadata' => $metadata]);

            return $restoreRun->refresh();
        }

        $preview = $service->preview($tenant, $backupSet, $selectedItemIds);

        $metadata = [
            'scope_mode' => $selectedItemIds === null ? 'all' : 'selected',
            'environment' => app()->environment('production') ? 'prod' : 'test',
            'highlander_label' => $highlanderLabel,
            'confirmed_at' => now()->toIso8601String(),
            'confirmed_by' => $actorEmail,
            'confirmed_by_name' => $actorName,
            'scope_basis' => $scopeBasis,
        ];

        if (is_array($checkSummary)) {
            $metadata['check_summary'] = $checkSummary;
        }

        if (is_array($checkResults)) {
            $metadata['check_results'] = $checkResults;
        }

        if (is_string($checksRanAt) && $checksRanAt !== '') {
            $metadata['checks_ran_at'] = $checksRanAt;
        }

        if (is_array($previewSummary)) {
            $metadata['preview_summary'] = $previewSummary;
        }

        if (is_array($previewDiffs)) {
            $metadata['preview_diffs'] = $previewDiffs;
        }

        if (is_string($previewRanAt) && $previewRanAt !== '') {
            $metadata['preview_ran_at'] = $previewRanAt;
        }

        if (is_array($checkBasis)) {
            $metadata['check_basis'] = $checkBasis;
        }

        if (is_array($previewBasis)) {
            $metadata['preview_basis'] = $previewBasis;
        }

        $metadata['execution_safety_snapshot'] = $restoreSafetyResolver
            ->executionSafetySnapshot($tenant, $user, $data)
            ->toArray();

        try {
            [$result, $restoreRun] = static::startQueuedRestoreExecution(
                tenant: $tenant,
                backupSet: $backupSet,
                selectedItemIds: $selectedItemIds,
                preview: $preview,
                metadata: $metadata,
                groupMapping: $groupMapping,
                actorEmail: $actorEmail,
                actorName: $actorName,
            );
        } catch (OperationalControlBlockedException $exception) {
            Notification::make()
                ->title($exception->title())
                ->body($exception->getMessage())
                ->warning()
                ->send();

            throw new \Filament\Support\Exceptions\Halt;
        }

        app(ProviderOperationStartResultPresenter::class)
            ->notification(
                result: $result,
                blockedTitle: 'Restore execution blocked',
                runUrl: OperationRunLinks::view($result->run, $tenant),
            )
            ->send();

        if (! in_array($result->status, ['started', 'deduped'], true)) {
            throw new \Filament\Support\Exceptions\Halt;
        }

        if (! $restoreRun instanceof RestoreRun) {
            throw new \RuntimeException('Restore execution was accepted without creating a restore run.');
        }

        return $restoreRun;
    }

    /**
     * @param  array<int>|null  $selectedItemIds
     * @param  array<string, mixed>  $preview
     * @param  array<string, mixed>  $metadata
     * @param  array<string, mixed>  $groupMapping
     * @return array{0: \App\Services\Providers\ProviderOperationStartResult, 1: ?RestoreRun}
     */
    private static function startQueuedRestoreExecution(
        ManagedEnvironment $tenant,
        BackupSet $backupSet,
        ?array $selectedItemIds,
        array $preview,
        array $metadata,
        array $groupMapping,
        ?string $actorEmail,
        ?string $actorName,
    ): array {
        $idempotencyKey = RestoreRunIdempotency::restoreExecuteKey(
            tenantId: (int) $tenant->getKey(),
            backupSetId: (int) $backupSet->getKey(),
            selectedItemIds: $selectedItemIds,
            groupMapping: $groupMapping,
        );

        $initiator = auth()->user();
        $initiator = $initiator instanceof User ? $initiator : null;

        static::guardRestoreExecutionOperationalControl(
            tenant: $tenant,
            backupSet: $backupSet,
            selectedItemIds: $selectedItemIds,
            initiator: $initiator,
        );

        $queuedRestoreRun = null;

        $dispatcher = function (OperationRun $run) use (
            $tenant,
            $backupSet,
            $selectedItemIds,
            $preview,
            $metadata,
            $groupMapping,
            $actorEmail,
            $actorName,
            $idempotencyKey,
            &$queuedRestoreRun,
        ): void {
            $queuedRestoreRun = RestoreRun::create([
                'managed_environment_id' => $tenant->id,
                'backup_set_id' => $backupSet->id,
                'operation_run_id' => $run->getKey(),
                'requested_by' => $actorEmail,
                'is_dry_run' => false,
                'status' => RestoreRunStatus::Queued->value,
                'idempotency_key' => $idempotencyKey,
                'requested_items' => $selectedItemIds,
                'preview' => $preview,
                'metadata' => $metadata,
                'group_mapping' => $groupMapping !== [] ? $groupMapping : null,
            ]);

            $context = is_array($run->context) ? $run->context : [];
            $context['restore_run_id'] = (int) $queuedRestoreRun->getKey();
            $run->forceFill(['context' => $context])->save();

            app(AuditLogger::class)->log(
                tenant: $tenant,
                action: 'restore.queued',
                context: [
                    'metadata' => [
                        'restore_run_id' => $queuedRestoreRun->id,
                        'backup_set_id' => $backupSet->id,
                    ],
                ],
                actorEmail: $actorEmail,
                actorName: $actorName,
                resourceType: 'restore_run',
                resourceId: (string) $queuedRestoreRun->id,
                status: 'success',
            );

            $providerConnectionId = is_numeric($context['provider_connection_id'] ?? null)
                ? (int) $context['provider_connection_id']
                : null;

            ExecuteRestoreRunJob::dispatch(
                restoreRunId: (int) $queuedRestoreRun->getKey(),
                actorEmail: $actorEmail,
                actorName: $actorName,
                operationRun: $run,
                providerConnectionId: $providerConnectionId,
            )->afterCommit();
        };

        if (static::requiresProviderExecution($backupSet, $selectedItemIds)) {
            $result = app(ProviderOperationStartGate::class)->start(
                tenant: $tenant,
                connection: null,
                operationType: 'restore.execute',
                dispatcher: $dispatcher,
                initiator: $initiator,
                extraContext: [
                    'backup_set_id' => (int) $backupSet->getKey(),
                    'is_dry_run' => false,
                    'execution_authority_mode' => 'actor_bound',
                    'required_capability' => Capabilities::TENANT_MANAGE,
                ],
            );
        } else {
            $run = app(OperationRunService::class)->ensureRunWithIdentity(
                tenant: $tenant,
                type: 'restore.execute',
                identityInputs: [
                    'idempotency_key' => $idempotencyKey,
                ],
                context: [
                    'backup_set_id' => (int) $backupSet->getKey(),
                    'is_dry_run' => false,
                    'execution_authority_mode' => 'actor_bound',
                    'required_capability' => Capabilities::TENANT_MANAGE,
                    'target_scope' => [
                        'entra_tenant_id' => $tenant->graphTenantId(),
                    ],
                ],
                initiator: $initiator,
            );

            if ($run->wasRecentlyCreated) {
                $dispatcher($run);

                $result = ProviderOperationStartResult::started($run, true);
            } else {
                $result = ProviderOperationStartResult::deduped($run);
            }
        }

        if (! $queuedRestoreRun instanceof RestoreRun && $result->status === 'deduped') {
            $restoreRunId = data_get($result->run->context ?? [], 'restore_run_id');

            if (is_numeric($restoreRunId)) {
                $queuedRestoreRun = RestoreRun::query()->whereKey((int) $restoreRunId)->first();
            }

            $queuedRestoreRun ??= RestoreRunIdempotency::findActiveRestoreRun(
                (int) $tenant->getKey(),
                $idempotencyKey,
            );
        }

        return [$result, $queuedRestoreRun?->refresh()];
    }

    /**
     * @param  array<int>|null  $selectedItemIds
     */
    private static function guardRestoreExecutionOperationalControl(
        ManagedEnvironment $tenant,
        BackupSet $backupSet,
        ?array $selectedItemIds,
        ?User $initiator,
    ): void {
        $workspace = $tenant->workspace;

        if (! $workspace instanceof Workspace) {
            throw new \RuntimeException('Restore execution requires a workspace context.');
        }

        $decision = app(OperationalControlEvaluator::class)->evaluate('restore.execute', $workspace);

        if (! $decision->isPaused()) {
            return;
        }

        app(WorkspaceAuditLogger::class)->log(
            workspace: $workspace,
            action: AuditActionId::OperationalControlExecutionBlocked,
            context: [
                'metadata' => array_filter([
                    'control_key' => $decision->controlKey,
                    'scope_type' => $decision->matchedScopeType,
                    'workspace_id' => (int) $workspace->getKey(),
                    'reason_text' => $decision->reasonText,
                    'expires_at' => $decision->expiresAt?->toIso8601String(),
                    'actor_id' => $initiator?->getKey(),
                    'backup_set_id' => (int) $backupSet->getKey(),
                    'selected_item_count' => is_array($selectedItemIds) ? count($selectedItemIds) : null,
                    'requested_scope' => 'restore.execute',
                ], static fn (mixed $value): bool => $value !== null && $value !== ''),
            ],
            actor: $initiator,
            status: 'blocked',
            resourceType: 'operational_control',
            resourceId: $decision->sourceActivationId !== null ? (string) $decision->sourceActivationId : null,
            targetLabel: OperationCatalog::label('restore.execute'),
            summary: 'Restore execution blocked by operational control',
            tenant: $tenant,
        );

        throw OperationalControlBlockedException::forDecision(
            decision: $decision,
            actionLabel: OperationCatalog::label('restore.execute'),
        );
    }

    /**
     * @param  array<int>|null  $selectedItemIds
     */
    private static function requiresProviderExecution(BackupSet $backupSet, ?array $selectedItemIds): bool
    {
        $query = $backupSet->items()->select(['id', 'policy_type']);

        if (is_array($selectedItemIds) && $selectedItemIds !== []) {
            $query->whereIn('id', $selectedItemIds);
        }

        return $query->get()->contains(function (BackupItem $item): bool {
            $restoreMode = static::typeMeta($item->policy_type)['restore'] ?? 'preview-only';

            return $restoreMode !== 'preview-only';
        });
    }

    /**
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    public static function synchronizeRestoreSafetyDraft(array $data): array
    {
        $resolver = static::restoreSafetyResolver();
        $scope = $resolver->scopeFingerprintFromData($data);

        $data['scope_basis'] = $resolver->scopeBasisFromData($data);
        $data['check_invalidation_reasons'] = $resolver->invalidationReasonsForBasis(
            currentScope: $scope,
            basis: is_array($data['check_basis'] ?? null) ? $data['check_basis'] : null,
            explicitReasons: $data['check_invalidation_reasons'] ?? null,
        );
        $data['preview_invalidation_reasons'] = $resolver->invalidationReasonsForBasis(
            currentScope: $scope,
            basis: is_array($data['preview_basis'] ?? null) ? $data['preview_basis'] : null,
            explicitReasons: $data['preview_invalidation_reasons'] ?? null,
        );

        return $data;
    }

    /**
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    private static function wizardSafetyState(array $data): array
    {
        $data = static::synchronizeRestoreSafetyDraft($data);
        $resolver = static::restoreSafetyResolver();
        $tenant = static::resolveTenantContextForCurrentPanel();
        $user = auth()->user();
        $scope = $resolver->scopeFingerprintFromData($data)->toArray();
        $previewIntegrity = $resolver->previewIntegrityFromData($data);
        $checksIntegrity = $resolver->checksIntegrityFromData($data);

        if (! $tenant instanceof ManagedEnvironment || ! $user instanceof User) {
            return [
                'currentScope' => $scope,
                'previewIntegrity' => $previewIntegrity->toArray(),
                'checksIntegrity' => $checksIntegrity->toArray(),
                'executionReadiness' => null,
                'safetyAssessment' => null,
            ];
        }

        $assessment = $resolver->safetyAssessment($tenant, $user, $data);

        return [
            'currentScope' => $scope,
            'previewIntegrity' => $previewIntegrity->toArray(),
            'checksIntegrity' => $checksIntegrity->toArray(),
            'executionReadiness' => $assessment->executionReadiness->toArray(),
            'safetyAssessment' => $assessment->toArray(),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private static function draftDataSnapshot(Get $get): array
    {
        return [
            'backup_set_id' => $get('backup_set_id'),
            'scope_mode' => $get('scope_mode'),
            'backup_item_ids' => $get('backup_item_ids'),
            'group_mapping' => static::normalizeGroupMapping($get('group_mapping')),
            'check_summary' => $get('check_summary'),
            'check_results' => $get('check_results'),
            'checks_ran_at' => $get('checks_ran_at'),
            'check_basis' => $get('check_basis'),
            'check_invalidation_reasons' => $get('check_invalidation_reasons'),
            'preview_summary' => $get('preview_summary'),
            'preview_diffs' => $get('preview_diffs'),
            'preview_ran_at' => $get('preview_ran_at'),
            'preview_basis' => $get('preview_basis'),
            'preview_invalidation_reasons' => $get('preview_invalidation_reasons'),
            'scope_basis' => $get('scope_basis'),
            'is_dry_run' => $get('is_dry_run'),
        ];
    }

    /**
     * @return array{
     *     preview: array<int|string, mixed>,
     *     previewIntegrity: array<string, mixed>,
     *     checksIntegrity: array<string, mixed>,
     *     executionSafetySnapshot: array<string, mixed>,
     *     scopeBasis: array<string, mixed>
     * }
     */
    private static function detailPreviewState(RestoreRun $record): array
    {
        $resolver = static::restoreSafetyResolver();
        $data = [
            'backup_set_id' => $record->backup_set_id,
            'scope_mode' => (string) (($record->scopeBasis()['scope_mode'] ?? null) ?: ((is_array($record->requested_items) && $record->requested_items !== []) ? 'selected' : 'all')),
            'backup_item_ids' => is_array($record->requested_items) ? $record->requested_items : [],
            'group_mapping' => is_array($record->group_mapping) ? $record->group_mapping : [],
            'preview_basis' => $record->previewBasis(),
            'check_basis' => $record->checkBasis(),
            'check_summary' => is_array(($record->metadata ?? [])['check_summary'] ?? null) ? $record->metadata['check_summary'] : [],
            'checks_ran_at' => $record->checkBasis()['ran_at'] ?? (($record->metadata ?? [])['checks_ran_at'] ?? null),
            'preview_summary' => is_array(($record->metadata ?? [])['preview_summary'] ?? null) ? $record->metadata['preview_summary'] : [],
            'preview_ran_at' => $record->previewBasis()['generated_at'] ?? (($record->metadata ?? [])['preview_ran_at'] ?? null),
        ];

        return [
            'preview' => is_array($record->preview) ? $record->preview : [],
            'previewIntegrity' => $resolver->previewIntegrityFromData($data)->toArray(),
            'checksIntegrity' => $resolver->checksIntegrityFromData($data)->toArray(),
            'executionSafetySnapshot' => $record->executionSafetySnapshot(),
            'scopeBasis' => $record->scopeBasis(),
        ];
    }

    /**
     * @return array{
     *     results: array<string, mixed>|array<int|string, mixed>,
     *     resultAttention: array<string, mixed>,
     *     executionSafetySnapshot: array<string, mixed>
     * }
     */
    private static function detailResultsState(RestoreRun $record): array
    {
        return [
            'results' => is_array($record->results) ? $record->results : [],
            'resultAttention' => static::restoreSafetyResolver()->resultAttentionForRun($record)->toArray(),
            'executionSafetySnapshot' => $record->executionSafetySnapshot(),
        ];
    }

    private static function restoreSafetyResolver(): RestoreSafetyResolver
    {
        return app(RestoreSafetyResolver::class);
    }

    /**
     * @param  array<int>|null  $selectedItemIds
     * @return array<int, array{id:string,label:string}>
     */
    private static function unresolvedGroups(?int $backupSetId, ?array $selectedItemIds, ManagedEnvironment $tenant): array
    {
        if (! $backupSetId) {
            return [];
        }

        $query = BackupItem::query()->where('backup_set_id', $backupSetId);

        if ($selectedItemIds !== null) {
            $query->whereIn('id', $selectedItemIds);
        }

        $items = $query->get(['assignments']);
        $assignments = [];
        $sourceNames = [];

        foreach ($items as $item) {
            if (! is_array($item->assignments) || $item->assignments === []) {
                continue;
            }

            foreach ($item->assignments as $assignment) {
                if (! is_array($assignment)) {
                    continue;
                }

                $target = $assignment['target'] ?? [];
                $odataType = $target['@odata.type'] ?? '';

                if (! in_array($odataType, [
                    '#microsoft.graph.groupAssignmentTarget',
                    '#microsoft.graph.exclusionGroupAssignmentTarget',
                ], true)) {
                    continue;
                }

                $groupId = $target['groupId'] ?? null;

                if (! is_string($groupId) || $groupId === '') {
                    continue;
                }

                $assignments[] = $groupId;
                $displayName = $target['group_display_name'] ?? null;

                if (is_string($displayName) && $displayName !== '') {
                    $sourceNames[$groupId] = $displayName;
                }
            }
        }

        $groupIds = array_values(array_unique($assignments));

        if ($groupIds === []) {
            return [];
        }

        $resolver = app(EntraGroupLabelResolver::class);
        $cached = $resolver->lookupMany($tenant, $groupIds);

        return array_map(function (string $groupId) use ($sourceNames, $cached): array {
            $cachedName = $cached[strtolower($groupId)] ?? null;
            $fallbackName = $cachedName ?? ($sourceNames[$groupId] ?? null);

            return [
                'id' => $groupId,
                'label' => EntraGroupLabelResolver::formatLabel($fallbackName, $groupId),
            ];
        }, $groupIds);
    }

    /**
     * @param  array<int>|null  $selectedItemIds
     * @return array<string, string|null>
     */
    private static function groupMappingPlaceholders(?int $backupSetId, string $scopeMode, ?array $selectedItemIds, ?ManagedEnvironment $tenant): array
    {
        if (! $tenant || ! $backupSetId) {
            return [];
        }

        if ($scopeMode === 'selected' && ($selectedItemIds === null || $selectedItemIds === [])) {
            return [];
        }

        $unresolved = static::unresolvedGroups(
            backupSetId: $backupSetId,
            selectedItemIds: $scopeMode === 'selected' ? $selectedItemIds : null,
            tenant: $tenant,
        );

        $placeholders = [];

        foreach ($unresolved as $group) {
            $groupId = $group['id'] ?? null;

            if (! is_string($groupId) || $groupId === '') {
                continue;
            }

            $placeholders[$groupId] = null;
        }

        return $placeholders;
    }

    /**
     * @return array<string, string>
     */
    private static function normalizeGroupMapping(mixed $mapping): array
    {
        if ($mapping instanceof \Illuminate\Contracts\Support\Arrayable) {
            $mapping = $mapping->toArray();
        }

        if ($mapping instanceof \stdClass) {
            $mapping = (array) $mapping;
        }

        if (! is_array($mapping)) {
            return [];
        }

        $result = [];

        if (array_key_exists('group_mapping', $mapping)) {
            $nested = $mapping['group_mapping'];

            if ($nested instanceof \Illuminate\Contracts\Support\Arrayable) {
                $nested = $nested->toArray();
            }

            if ($nested instanceof \stdClass) {
                $nested = (array) $nested;
            }

            if (is_array($nested)) {
                $mapping = $nested;
            }
        }

        foreach ($mapping as $key => $value) {
            if (! is_string($key) || $key === '') {
                continue;
            }

            $sourceGroupId = str_starts_with($key, 'group_mapping.')
                ? substr($key, strlen('group_mapping.'))
                : $key;

            if ($sourceGroupId === '') {
                continue;
            }

            if ($value instanceof BackedEnum) {
                $value = $value->value;
            }

            if (is_array($value) || $value instanceof \stdClass) {
                $value = (array) $value;
                $value = $value['value'] ?? $value['id'] ?? null;
            }

            if (is_string($value)) {
                $value = trim($value);
                $result[$sourceGroupId] = $value !== '' ? $value : null;

                continue;
            }

            $result[$sourceGroupId] = null;
        }

        return array_filter($result, static fn (?string $value): bool => is_string($value) && $value !== '');
    }

    /**
     * Build the rerun table action with UiEnforcement + write gate disabled state.
     *
     * UiEnforcement::apply() overrides ->disabled() and ->tooltip(), so the gate
     * check must compose on top of the enforcement action AFTER apply(). This method
     * extracts the rerun action into its own builder to keep the table definition clean.
     */
    private static function rerunActionWithGate(): Actions\Action|BulkAction
    {
        /** @var Actions\Action $action */
        $action = UiEnforcement::forTableAction(
            Actions\Action::make('rerun')
                ->label('Rerun')
                ->icon('heroicon-o-arrow-path')
                ->color('primary')
                ->requiresConfirmation()
                ->visible(function (RestoreRun $record): bool {
                    $backupSet = $record->backupSet;

                    return ! $record->trashed()
                        && $record->isDeletable()
                        && $backupSet !== null
                        && ! $backupSet->trashed();
                })
                ->action(function (
                    RestoreRun $record,
                    RestoreService $restoreService,
                    \App\Services\Intune\AuditLogger $auditLogger,
                    HasTable $livewire
                ) {
                    $record = static::resolveProtectedRestoreRunRecordOrFail($record);
                    $tenant = $record->tenant;
                    $backupSet = $record->backupSet;

                    if ($record->trashed() || ! $tenant || ! $backupSet || $backupSet->trashed()) {
                        Notification::make()
                            ->title('Restore run cannot be rerun')
                            ->body('Restore run or backup set is archived or unavailable.')
                            ->warning()
                            ->send();

                        return;
                    }

                    try {
                        app(WriteGateInterface::class)->evaluate($tenant, 'restore.rerun');
                    } catch (ProviderAccessHardeningRequired $e) {
                        app(\App\Services\Intune\AuditLogger::class)->log(
                            tenant: $tenant,
                            action: 'intune_rbac.write_blocked',
                            status: 'blocked',
                            actorEmail: auth()->user()?->email,
                            actorName: auth()->user()?->name,
                            resourceType: 'restore_run',
                            resourceId: (string) $record->getKey(),
                            context: [
                                'metadata' => [
                                    'operation_type' => 'restore.rerun',
                                    'reason_code' => $e->reasonCode,
                                    'backup_set_id' => $backupSet?->getKey(),
                                    'original_restore_run_id' => $record->getKey(),
                                ],
                            ],
                        );

                        Notification::make()
                            ->title('Write operation blocked')
                            ->body($e->reasonMessage)
                            ->danger()
                            ->send();

                        return;
                    }

                    if (! (bool) $record->is_dry_run) {
                        $selectedItemIds = is_array($record->requested_items) ? $record->requested_items : null;
                        $groupMapping = is_array($record->group_mapping) ? $record->group_mapping : [];
                        $actorEmail = auth()->user()?->email;
                        $actorName = auth()->user()?->name;
                        $tenantIdentifier = $tenant->managed_environment_id ?? $tenant->external_id;
                        $highlanderLabel = (string) ($tenant->name ?? $tenantIdentifier ?? $tenant->getKey());

                        $preview = $restoreService->preview($tenant, $backupSet, $selectedItemIds);
                        $metadata = [
                            'scope_mode' => $selectedItemIds === null ? 'all' : 'selected',
                            'environment' => app()->environment('production') ? 'prod' : 'test',
                            'highlander_label' => $highlanderLabel,
                            'confirmed_at' => now()->toIso8601String(),
                            'confirmed_by' => $actorEmail,
                            'confirmed_by_name' => $actorName,
                            'rerun_of_restore_run_id' => $record->id,
                        ];

                        $metadata['rerun_of_restore_run_id'] = $record->id;

                        try {
                            [$result, $newRun] = static::startQueuedRestoreExecution(
                                tenant: $tenant,
                                backupSet: $backupSet,
                                selectedItemIds: $selectedItemIds,
                                preview: $preview,
                                metadata: $metadata,
                                groupMapping: $groupMapping,
                                actorEmail: $actorEmail,
                                actorName: $actorName,
                            );
                        } catch (OperationalControlBlockedException $exception) {
                            Notification::make()
                                ->title($exception->title())
                                ->body($exception->getMessage())
                                ->warning()
                                ->send();

                            return;
                        }

                        if (in_array($result->status, ['started', 'deduped', 'scope_busy'], true)) {
                            OpsUxBrowserEvents::dispatchRunEnqueued($livewire);
                        }

                        app(ProviderOperationStartResultPresenter::class)
                            ->notification(
                                result: $result,
                                blockedTitle: 'Restore execution blocked',
                                runUrl: OperationRunLinks::view($result->run, $tenant),
                            )
                            ->send();

                        if ($result->status !== 'started' || ! $newRun instanceof RestoreRun) {
                            return;
                        }

                        $auditLogger->log(
                            tenant: $tenant,
                            action: 'restore_run.rerun',
                            resourceType: 'restore_run',
                            resourceId: (string) $newRun->id,
                            status: 'success',
                            context: [
                                'metadata' => [
                                    'original_restore_run_id' => $record->id,
                                    'backup_set_id' => $backupSet->id,
                                ],
                            ],
                            actorEmail: $actorEmail,
                            actorName: $actorName,
                        );

                        return;
                    }

                    try {
                        $newRun = $restoreService->execute(
                            tenant: $tenant,
                            backupSet: $backupSet,
                            selectedItemIds: $record->requested_items ?? null,
                            dryRun: (bool) $record->is_dry_run,
                            actorEmail: auth()->user()?->email,
                            actorName: auth()->user()?->name,
                            groupMapping: $record->group_mapping ?? []
                        );
                    } catch (\Throwable $throwable) {
                        Notification::make()
                            ->title('Restore run failed to start')
                            ->body($throwable->getMessage())
                            ->danger()
                            ->send();

                        return;
                    }

                    $auditLogger->log(
                        tenant: $tenant,
                        action: 'restore_run.rerun',
                        resourceType: 'restore_run',
                        resourceId: (string) $newRun->id,
                        status: 'success',
                        context: [
                            'metadata' => [
                                'original_restore_run_id' => $record->id,
                                'backup_set_id' => $backupSet->id,
                            ],
                        ]
                    );

                    OpsUxBrowserEvents::dispatchRunEnqueued($livewire);
                    OperationUxPresenter::queuedToast('restore.execute')
                        ->send();
                }),
            fn () => static::resolveTenantContextForCurrentPanel(),
        )
            ->requireCapability(Capabilities::TENANT_MANAGE)
            ->preserveVisibility()
            ->apply();

        // Compose write gate disabled/tooltip on top of UiEnforcement's RBAC check.
        // UiEnforcement::apply() sets its own ->disabled() / ->tooltip();
        // we override here to merge both concerns.
        $action->disabled(function (?Model $record = null): bool {
            $user = auth()->user();

            if (! $user instanceof User) {
                return true;
            }

            $tenant = $record instanceof RestoreRun ? $record->tenant : static::resolveTenantContextForCurrentPanel();

            if (! $tenant instanceof ManagedEnvironment) {
                return true;
            }

            // Check RBAC capability first (mirrors UiEnforcement logic)
            $resolver = app(CapabilityResolver::class);

            if (! $resolver->can($user, $tenant, Capabilities::TENANT_MANAGE)) {
                return true;
            }

            // Then check write gate
            return app(WriteGateInterface::class)->wouldBlock($tenant);
        });

        $action->tooltip(function (?Model $record = null): ?string {
            $user = auth()->user();

            if (! $user instanceof User) {
                return \App\Support\Auth\UiTooltips::insufficientPermission();
            }

            $tenant = $record instanceof RestoreRun ? $record->tenant : static::resolveTenantContextForCurrentPanel();

            if (! $tenant instanceof ManagedEnvironment) {
                return 'ManagedEnvironment unavailable';
            }

            // Check RBAC capability first
            $resolver = app(CapabilityResolver::class);

            if (! $resolver->can($user, $tenant, Capabilities::TENANT_MANAGE)) {
                return \App\Support\Auth\UiTooltips::insufficientPermission();
            }

            // Then check write gate
            try {
                app(WriteGateInterface::class)->evaluate($tenant, 'restore.rerun');
            } catch (ProviderAccessHardeningRequired $e) {
                return $e->reasonMessage;
            }

            return null;
        });

        return $action;
    }
}
