<?php

namespace App\Filament\Resources;

use App\Filament\Resources\PolicyResource\Pages;
use App\Filament\Resources\PolicyResource\RelationManagers\VersionsRelationManager;
use App\Jobs\BulkPolicyDeleteJob;
use App\Jobs\BulkPolicyExportJob;
use App\Jobs\BulkPolicyUnignoreJob;
use App\Jobs\SyncPoliciesJob;
use App\Models\Policy;
use App\Models\Tenant;
use App\Models\User;
use App\Services\Auth\CapabilityResolver;
use App\Services\Intune\PolicyNormalizer;
use App\Services\OperationRunService;
use App\Services\Operations\BulkSelectionIdentity;
use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Badges\TagBadgeDomain;
use App\Support\Badges\TagBadgeRenderer;
use App\Support\Filament\TablePaginationProfiles;
use App\Support\OperationRunLinks;
use App\Support\OpsUx\OperationUxPresenter;
use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\Rbac\UiEnforcement;
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\Components\TextEntry;
use Filament\Infolists\Components\ViewEntry;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Schema;
use Filament\Tables;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use UnitEnum;

class PolicyResource extends Resource
{
    protected static ?string $model = Policy::class;

    protected static ?string $tenantOwnershipRelationshipName = 'tenant';

    protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-shield-check';

    protected static string|UnitEnum|null $navigationGroup = 'Inventory';

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

        if (! $tenant instanceof Tenant || ! $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 actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forResource(ActionSurfaceProfile::CrudListAndView)
            ->satisfy(ActionSurfaceSlot::ListHeader, 'Header action: Sync from Intune.')
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ViewAction->value)
            ->satisfy(ActionSurfaceSlot::ListRowMoreMenu, 'Secondary row actions are grouped under "More".')
            ->satisfy(ActionSurfaceSlot::ListBulkMoreGroup, 'Bulk actions are grouped under "More".')
            ->satisfy(ActionSurfaceSlot::ListEmptyState, 'List page defines an empty-state CTA.')
            ->satisfy(ActionSurfaceSlot::DetailHeader, 'View page provides header actions when applicable.');
    }

    public static function makeSyncAction(string $name = 'sync'): Actions\Action
    {
        return UiEnforcement::forAction(
            Actions\Action::make($name)
                ->label('Sync from Intune')
                ->icon('heroicon-o-arrow-path')
                ->color('primary')
                ->requiresConfirmation()
                ->modalHeading('Sync policies from Intune')
                ->modalDescription('This queues a background sync operation for supported policy types in the current tenant.')
                ->action(function (Pages\ListPolicies $livewire): void {
                    $tenant = Tenant::current();
                    $user = auth()->user();

                    if (! $user instanceof User || ! $tenant instanceof Tenant) {
                        abort(404);
                    }

                    $requestedTypes = array_map(
                        static fn (array $typeConfig): string => (string) $typeConfig['type'],
                        config('tenantpilot.supported_policy_types', [])
                    );

                    sort($requestedTypes);

                    /** @var OperationRunService $opService */
                    $opService = app(OperationRunService::class);
                    $opRun = $opService->ensureRun(
                        tenant: $tenant,
                        type: 'policy.sync',
                        inputs: [
                            'scope' => 'all',
                            'types' => $requestedTypes,
                        ],
                        initiator: $user
                    );

                    if (! $opRun->wasRecentlyCreated && in_array($opRun->status, ['queued', 'running'], true)) {
                        OpsUxBrowserEvents::dispatchRunEnqueued($livewire);
                        OperationUxPresenter::alreadyQueuedToast((string) $opRun->type)
                            ->actions([
                                Actions\Action::make('view_run')
                                    ->label('View run')
                                    ->url(OperationRunLinks::view($opRun, $tenant)),
                            ])
                            ->send();

                        return;
                    }

                    $opService->dispatchOrFail($opRun, function () use ($tenant, $requestedTypes, $opRun): void {
                        SyncPoliciesJob::dispatch((int) $tenant->getKey(), $requestedTypes, null, $opRun);
                    });
                    OpsUxBrowserEvents::dispatchRunEnqueued($livewire);
                    OperationUxPresenter::queuedToast((string) $opRun->type)
                        ->actions([
                            Actions\Action::make('view_run')
                                ->label('View run')
                                ->url(OperationRunLinks::view($opRun, $tenant)),
                        ])
                        ->send();
                })
        )
            ->requireCapability(Capabilities::TENANT_SYNC)
            ->tooltip('You do not have permission to sync policies.')
            ->apply();
    }

    public static function form(Schema $schema): Schema
    {
        return $schema;
    }

    public static function infolist(Schema $schema): Schema
    {
        return $schema
            ->schema([
                Section::make('Policy Details')
                    ->schema([
                        TextEntry::make('display_name')->label('Policy'),
                        TextEntry::make('policy_type')->label('Type'),
                        TextEntry::make('platform'),
                        TextEntry::make('external_id')->label('External ID'),
                        TextEntry::make('last_synced_at')->dateTime()->label('Last synced'),
                        TextEntry::make('created_at')->since(),
                        TextEntry::make('latest_snapshot_mode')
                            ->label('Snapshot')
                            ->badge()
                            ->formatStateUsing(BadgeRenderer::label(BadgeDomain::PolicySnapshotMode))
                            ->color(BadgeRenderer::color(BadgeDomain::PolicySnapshotMode))
                            ->icon(BadgeRenderer::icon(BadgeDomain::PolicySnapshotMode))
                            ->iconColor(BadgeRenderer::iconColor(BadgeDomain::PolicySnapshotMode))
                            ->state(fn (Policy $record): string => (static::latestVersionMetadata($record)['source'] ?? null) === 'metadata_only' ? 'metadata_only' : 'full')
                            ->helperText(function (Policy $record): ?string {
                                $meta = static::latestVersionMetadata($record);

                                if (($meta['source'] ?? null) !== 'metadata_only') {
                                    return null;
                                }

                                $status = $meta['original_status'] ?? null;

                                return sprintf(
                                    'Graph returned %s for this policy type. Only local metadata was saved; settings and restore are unavailable until Graph works again.',
                                    $status ?? 'an error'
                                );
                            })
                            ->visible(fn (Policy $record) => $record->versions()->exists()),
                    ])
                    ->columns(2)
                    ->columnSpanFull(),

                // Tabbed content (General / Settings / JSON)
                Tabs::make('policy_content')
                    ->activeTab(1)
                    ->persistTabInQueryString()
                    ->tabs([
                        Tab::make('General')
                            ->id('general')
                            ->schema([
                                ViewEntry::make('policy_general')
                                    ->label('')
                                    ->view('filament.infolists.entries.policy-general')
                                    ->state(function (Policy $record) {
                                        return static::generalOverviewState($record);
                                    }),
                            ])
                            ->visible(fn (Policy $record) => $record->versions()->exists()),
                        Tab::make('Settings')
                            ->id('settings')
                            ->schema([
                                ViewEntry::make('settings_catalog')
                                    ->label('')
                                    ->view('filament.infolists.entries.normalized-settings')
                                    ->state(function (Policy $record) {
                                        return static::settingsTabState($record);
                                    })
                                    ->visible(fn (Policy $record) => static::hasSettingsTable($record) &&
                                        $record->versions()->exists()
                                    ),

                                ViewEntry::make('settings_standard')
                                    ->label('')
                                    ->view('filament.infolists.entries.policy-settings-standard')
                                    ->state(function (Policy $record) {
                                        return static::settingsTabState($record);
                                    })
                                    ->visible(fn (Policy $record) => ! static::hasSettingsTable($record) &&
                                        $record->versions()->exists()
                                    ),

                                TextEntry::make('no_settings_available')
                                    ->label('Settings')
                                    ->state('No policy snapshot available yet.')
                                    ->helperText('This policy has been inventoried but no configuration snapshot has been captured yet.')
                                    ->visible(fn (Policy $record) => ! $record->versions()->exists()),
                            ]),
                        Tab::make('JSON')
                            ->id('json')
                            ->schema([
                                ViewEntry::make('snapshot_json')
                                    ->view('filament.infolists.entries.snapshot-json')
                                    ->state(fn (Policy $record) => static::latestSnapshot($record))
                                    ->columnSpanFull(),
                                TextEntry::make('snapshot_size')
                                    ->label('Payload Size')
                                    ->state(fn (Policy $record) => strlen(json_encode(static::latestSnapshot($record) ?: [])))
                                    ->formatStateUsing(function ($state) {
                                        if ($state > 512000) {
                                            return '<span class="inline-flex items-center gap-1 text-warning-600 dark:text-warning-400 font-semibold">
                                                <svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
                                                    <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
                                                </svg>
                                                Large payload ('.number_format($state / 1024, 0).' KB) - May impact performance
                                            </span>';
                                        }

                                        return number_format($state / 1024, 1).' KB';
                                    })
                                    ->html()
                                    ->visible(fn (Policy $record) => strlen(json_encode(static::latestSnapshot($record) ?: [])) > 512000),
                            ])
                            ->visible(fn (Policy $record) => $record->versions()->exists()),
                    ])
                    ->columnSpanFull()
                    ->visible(fn (Policy $record) => static::usesTabbedLayout($record)),

                // Legacy layout (kept for fallback if tabs are disabled)
                Section::make('Settings')
                    ->schema([
                        ViewEntry::make('settings')
                            ->label('')
                            ->view('filament.infolists.entries.normalized-settings')
                            ->state(function (Policy $record) {
                                $normalized = app(PolicyNormalizer::class)->normalize(
                                    static::latestSnapshot($record),
                                    $record->policy_type ?? '',
                                    $record->platform
                                );

                                $normalized['context'] = 'policy';
                                $normalized['record_id'] = (string) $record->getKey();

                                return $normalized;
                            }),
                    ])
                    ->columnSpanFull()
                    ->visible(function (Policy $record) {
                        return ! static::usesTabbedLayout($record);
                    }),

                Section::make('Policy Snapshot (JSON)')
                    ->schema([
                        ViewEntry::make('snapshot_json')
                            ->view('filament.infolists.entries.snapshot-json')
                            ->state(fn (Policy $record) => static::latestSnapshot($record))
                            ->columnSpanFull(),

                        TextEntry::make('snapshot_size')
                            ->label('Payload Size')
                            ->state(fn (Policy $record) => strlen(json_encode(static::latestSnapshot($record) ?: [])))
                            ->formatStateUsing(function ($state) {
                                if ($state > 512000) {
                                    return '<span class="inline-flex items-center gap-1 text-warning-600 dark:text-warning-400 font-semibold">
                                        <svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
                                            <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
                                        </svg>
                                        Large payload ('.number_format($state / 1024, 0).' KB) - May impact performance
                                    </span>';
                                }

                                return number_format($state / 1024, 1).' KB';
                            })
                            ->html()
                            ->visible(fn (Policy $record) => strlen(json_encode(static::latestSnapshot($record) ?: [])) > 512000),
                    ])
                    ->collapsible()
                    ->collapsed(fn (Policy $record) => strlen(json_encode(static::latestSnapshot($record) ?: [])) > 512000)
                    ->description('Raw JSON configuration from Microsoft Graph API')
                    ->columnSpanFull()
                    ->visible(function (Policy $record) {
                        return ! static::usesTabbedLayout($record);
                    }),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->modifyQueryUsing(function (Builder $query) {
                // Quick-Workaround: Hide policies not synced in last 7 days
                // Full solution in Feature 005: Policy Lifecycle Management (soft delete)
                $query->where('last_synced_at', '>', now()->subDays(7));
            })
            ->defaultSort('display_name')
            ->paginated(TablePaginationProfiles::resource())
            ->persistFiltersInSession()
            ->persistSearchInSession()
            ->persistSortInSession()
            ->columns([
                Tables\Columns\TextColumn::make('display_name')
                    ->label('Policy')
                    ->searchable()
                    ->sortable(),
                Tables\Columns\TextColumn::make('policy_type')
                    ->label('Type')
                    ->badge()
                    ->formatStateUsing(TagBadgeRenderer::label(TagBadgeDomain::PolicyType))
                    ->color(TagBadgeRenderer::color(TagBadgeDomain::PolicyType))
                    ->icon(TagBadgeRenderer::icon(TagBadgeDomain::PolicyType))
                    ->iconColor(TagBadgeRenderer::iconColor(TagBadgeDomain::PolicyType)),
                Tables\Columns\TextColumn::make('category')
                    ->label('Category')
                    ->badge()
                    ->state(fn (Policy $record) => static::typeMeta($record->policy_type)['category'] ?? null)
                    ->formatStateUsing(TagBadgeRenderer::label(TagBadgeDomain::PolicyCategory))
                    ->color(TagBadgeRenderer::color(TagBadgeDomain::PolicyCategory)),
                Tables\Columns\TextColumn::make('restore_mode')
                    ->label('Restore')
                    ->badge()
                    ->state(fn (Policy $record) => static::typeMeta($record->policy_type)['restore'] ?? 'enabled')
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::PolicyRestoreMode))
                    ->color(BadgeRenderer::color(BadgeDomain::PolicyRestoreMode))
                    ->icon(BadgeRenderer::icon(BadgeDomain::PolicyRestoreMode))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::PolicyRestoreMode)),
                Tables\Columns\TextColumn::make('platform')
                    ->badge()
                    ->formatStateUsing(TagBadgeRenderer::label(TagBadgeDomain::Platform))
                    ->color(TagBadgeRenderer::color(TagBadgeDomain::Platform))
                    ->sortable(),
                Tables\Columns\TextColumn::make('settings_status')
                    ->label('Settings')
                    ->badge()
                    ->state(function (Policy $record) {
                        $latest = $record->versions->first();
                        $snapshot = $latest?->snapshot ?? [];
                        $hasSettings = is_array($snapshot) && ! empty($snapshot['settings']);

                        return $hasSettings ? 'Available' : 'Missing';
                    })
                    ->color(function (Policy $record) {
                        $latest = $record->versions->first();
                        $snapshot = $latest?->snapshot ?? [];
                        $hasSettings = is_array($snapshot) && ! empty($snapshot['settings']);

                        return $hasSettings ? 'success' : 'gray';
                    }),
                Tables\Columns\TextColumn::make('external_id')
                    ->label('External ID')
                    ->copyable()
                    ->limit(32)
                    ->toggleable(isToggledHiddenByDefault: true),
                Tables\Columns\TextColumn::make('last_synced_at')
                    ->label('Last synced')
                    ->dateTime()
                    ->sortable(),
                Tables\Columns\TextColumn::make('created_at')
                    ->dateTime()
                    ->since()
                    ->toggleable(isToggledHiddenByDefault: true),
            ])
            ->filters([
                Tables\Filters\SelectFilter::make('visibility')
                    ->label('Visibility')
                    ->options([
                        'active' => 'Active',
                        'ignored' => 'Ignored',
                    ])
                    ->default('active')
                    ->query(function (Builder $query, array $data) {
                        $value = $data['value'] ?? null;

                        if (blank($value)) {
                            return;
                        }

                        if ($value === 'active') {
                            $query->whereNull('ignored_at');

                            return;
                        }

                        if ($value === 'ignored') {
                            $query->whereNotNull('ignored_at');
                        }
                    }),
                Tables\Filters\SelectFilter::make('policy_type')
                    ->options(function () {
                        return collect(config('tenantpilot.supported_policy_types', []))
                            ->pluck('label', 'type')
                            ->map(fn ($label, $type) => $label ?? $type)
                            ->all();
                    }),
                Tables\Filters\SelectFilter::make('category')
                    ->options(function () {
                        return collect(config('tenantpilot.supported_policy_types', []))
                            ->pluck('category', 'category')
                            ->filter()
                            ->unique()
                            ->sort()
                            ->all();
                    })
                    ->query(function (Builder $query, array $data) {
                        $category = $data['value'] ?? null;
                        if (! $category) {
                            return;
                        }

                        $types = collect(config('tenantpilot.supported_policy_types', []))
                            ->where('category', $category)
                            ->pluck('type')
                            ->all();

                        $query->whereIn('policy_type', $types);
                    }),
                Tables\Filters\SelectFilter::make('platform')
                    ->options(fn () => Policy::query()
                        ->distinct()
                        ->pluck('platform', 'platform')
                        ->filter()
                        ->reject(fn ($platform) => is_string($platform) && strtolower($platform) === 'all')
                        ->all()),
            ])
            ->actions([
                Actions\ViewAction::make(),
                ActionGroup::make([
                    UiEnforcement::forTableAction(
                        Actions\Action::make('ignore')
                            ->label('Ignore')
                            ->icon('heroicon-o-trash')
                            ->color('danger')
                            ->requiresConfirmation()
                            ->visible(fn (Policy $record): bool => $record->ignored_at === null)
                            ->action(function (Policy $record): void {
                                $record->ignore();

                                Notification::make()
                                    ->title('Policy ignored')
                                    ->success()
                                    ->send();
                            }),
                        fn () => Tenant::current(),
                    )
                        ->requireCapability(Capabilities::TENANT_MANAGE)
                        ->tooltip('You do not have permission to ignore policies.')
                        ->preserveVisibility()
                        ->apply(),
                    UiEnforcement::forTableAction(
                        Actions\Action::make('restore')
                            ->label('Restore')
                            ->icon('heroicon-o-arrow-uturn-left')
                            ->color('success')
                            ->requiresConfirmation()
                            ->visible(fn (Policy $record): bool => $record->ignored_at !== null)
                            ->action(function (Policy $record): void {
                                $record->unignore();

                                Notification::make()
                                    ->title('Policy restored')
                                    ->success()
                                    ->send();
                            }),
                        fn () => Tenant::current(),
                    )
                        ->requireCapability(Capabilities::TENANT_MANAGE)
                        ->tooltip('You do not have permission to restore policies.')
                        ->preserveVisibility()
                        ->apply(),
                    UiEnforcement::forTableAction(
                        Actions\Action::make('sync')
                            ->label('Sync')
                            ->icon('heroicon-o-arrow-path')
                            ->color('primary')
                            ->requiresConfirmation()
                            ->visible(fn (Policy $record): bool => $record->ignored_at === null)
                            ->action(function (Policy $record, HasTable $livewire): void {
                                $tenant = Tenant::current();
                                $user = auth()->user();

                                if (! $tenant instanceof Tenant) {
                                    abort(404);
                                }

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

                                /** @var OperationRunService $opService */
                                $opService = app(OperationRunService::class);
                                $opRun = $opService->ensureRun(
                                    tenant: $tenant,
                                    type: 'policy.sync_one',
                                    inputs: [
                                        'scope' => 'one',
                                        'policy_id' => (int) $record->getKey(),
                                    ],
                                    initiator: $user
                                );

                                if (! $opRun->wasRecentlyCreated && in_array($opRun->status, ['queued', 'running'], true)) {
                                    OpsUxBrowserEvents::dispatchRunEnqueued($livewire);
                                    OperationUxPresenter::alreadyQueuedToast((string) $opRun->type)
                                        ->actions([
                                            Actions\Action::make('view_run')
                                                ->label('View run')
                                                ->url(OperationRunLinks::view($opRun, $tenant)),
                                        ])
                                        ->send();

                                    return;
                                }

                                SyncPoliciesJob::dispatch((int) $tenant->getKey(), null, [(int) $record->getKey()], $opRun);
                                OpsUxBrowserEvents::dispatchRunEnqueued($livewire);
                                OperationUxPresenter::queuedToast((string) $opRun->type)
                                    ->actions([
                                        Actions\Action::make('view_run')
                                            ->label('View run')
                                            ->url(OperationRunLinks::view($opRun, $tenant)),
                                    ])
                                    ->send();
                            }),
                        fn () => Tenant::current(),
                    )
                        ->requireCapability(Capabilities::TENANT_SYNC)
                        ->preserveVisibility()
                        ->apply(),
                    UiEnforcement::forTableAction(
                        Actions\Action::make('export')
                            ->label('Export to Backup')
                            ->icon('heroicon-o-archive-box-arrow-down')
                            ->visible(fn (Policy $record): bool => $record->ignored_at === null)
                            ->form([
                                Forms\Components\TextInput::make('backup_name')
                                    ->label('Backup Name')
                                    ->required()
                                    ->default(fn () => 'Backup '.now()->toDateTimeString()),
                            ])
                            ->action(function (Policy $record, array $data): void {
                                $tenant = Tenant::current();
                                $user = auth()->user();

                                if (! $tenant instanceof Tenant) {
                                    abort(404);
                                }

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

                                $ids = [(int) $record->getKey()];

                                /** @var BulkSelectionIdentity $selection */
                                $selection = app(BulkSelectionIdentity::class);

                                $selectionIdentity = $selection->fromIds($ids);

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

                                $opRun = $runs->enqueueBulkOperation(
                                    tenant: $tenant,
                                    type: 'policy.export',
                                    targetScope: [
                                        'entra_tenant_id' => (string) ($tenant->tenant_id ?? $tenant->external_id),
                                    ],
                                    selectionIdentity: $selectionIdentity,
                                    dispatcher: function ($operationRun) use ($tenant, $user, $ids, $data): void {
                                        BulkPolicyExportJob::dispatchSync(
                                            tenantId: (int) $tenant->getKey(),
                                            userId: (int) $user->getKey(),
                                            policyIds: $ids,
                                            backupName: (string) $data['backup_name'],
                                            operationRun: $operationRun,
                                        );
                                    },
                                    initiator: $user,
                                    extraContext: [
                                        'backup_name' => (string) $data['backup_name'],
                                        'policy_count' => 1,
                                    ],
                                    emitQueuedNotification: false,
                                );

                                OperationUxPresenter::queuedToast((string) $opRun->type)
                                    ->actions([
                                        Actions\Action::make('view_run')
                                            ->label('View run')
                                            ->url(OperationRunLinks::view($opRun, $tenant)),
                                    ])
                                    ->send();
                            }),
                        fn () => Tenant::current(),
                    )
                        ->requireCapability(Capabilities::TENANT_MANAGE)
                        ->preserveVisibility()
                        ->apply(),
                ])
                    ->label('More')
                    ->icon('heroicon-o-ellipsis-vertical'),
            ])
            ->bulkActions([
                BulkActionGroup::make([
                    UiEnforcement::forBulkAction(
                        BulkAction::make('bulk_delete')
                            ->label('Ignore Policies')
                            ->icon('heroicon-o-trash')
                            ->color('danger')
                            ->requiresConfirmation()
                            ->hidden(function (HasTable $livewire): bool {
                                $visibilityFilterState = $livewire->getTableFilterState('visibility') ?? [];
                                $value = $visibilityFilterState['value'] ?? null;

                                return $value === 'ignored';
                            })
                            ->form(function (Collection $records) {
                                if ($records->count() >= 20) {
                                    return [
                                        Forms\Components\TextInput::make('confirmation')
                                            ->label('Type DELETE to confirm')
                                            ->required()
                                            ->in(['DELETE'])
                                            ->validationMessages([
                                                'in' => 'Please type DELETE to confirm.',
                                            ]),
                                    ];
                                }

                                return [];
                            })
                            ->action(function (Collection $records, HasTable $livewire): void {
                                $tenant = Tenant::current();
                                $user = auth()->user();
                                $count = $records->count();
                                $ids = $records->pluck('id')->toArray();

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

                                /** @var BulkSelectionIdentity $selection */
                                $selection = app(BulkSelectionIdentity::class);

                                $selectionIdentity = $selection->fromIds($ids);

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

                                $opRun = $runs->enqueueBulkOperation(
                                    tenant: $tenant,
                                    type: 'policy.delete',
                                    targetScope: [
                                        'entra_tenant_id' => (string) ($tenant->tenant_id ?? $tenant->external_id),
                                    ],
                                    selectionIdentity: $selectionIdentity,
                                    dispatcher: function ($operationRun) use ($tenant, $user, $ids): void {
                                        BulkPolicyDeleteJob::dispatch(
                                            tenantId: (int) $tenant->getKey(),
                                            userId: (int) $user->getKey(),
                                            policyIds: $ids,
                                            operationRun: $operationRun,
                                        );
                                    },
                                    initiator: $user,
                                    extraContext: [
                                        'policy_count' => $count,
                                    ],
                                    emitQueuedNotification: false,
                                );

                                $runUrl = OperationRunLinks::view($opRun, $tenant);

                                OpsUxBrowserEvents::dispatchRunEnqueued($livewire);

                                if (! $opRun->wasRecentlyCreated && in_array($opRun->status, ['queued', 'running'], true)) {
                                    OperationUxPresenter::alreadyQueuedToast((string) $opRun->type)
                                        ->body("Queued deletion for {$count} policies.")
                                        ->actions([
                                            \Filament\Actions\Action::make('view_run')
                                                ->label('View run')
                                                ->url($runUrl),
                                        ])
                                        ->send();

                                    return;
                                }

                                OperationUxPresenter::queuedToast((string) $opRun->type)
                                    ->body("Queued deletion for {$count} policies.")
                                    ->actions([
                                        \Filament\Actions\Action::make('view_run')
                                            ->label('View run')
                                            ->url($runUrl),
                                    ])
                                    ->send();
                            })
                            ->deselectRecordsAfterCompletion(),
                    )
                        ->requireCapability(Capabilities::TENANT_MANAGE)
                        ->apply(),

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

                                return ! in_array($value, [null, 'ignored'], true);
                            })
                            ->action(function (Collection $records, HasTable $livewire): void {
                                $tenant = Tenant::current();
                                $user = auth()->user();
                                $count = $records->count();
                                $ids = $records->pluck('id')->toArray();

                                if (! $tenant instanceof Tenant) {
                                    abort(404);
                                }

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

                                /** @var BulkSelectionIdentity $selection */
                                $selection = app(BulkSelectionIdentity::class);

                                $selectionIdentity = $selection->fromIds($ids);

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

                                $opRun = $runs->enqueueBulkOperation(
                                    tenant: $tenant,
                                    type: 'policy.unignore',
                                    targetScope: [
                                        'entra_tenant_id' => (string) ($tenant->tenant_id ?? $tenant->external_id),
                                    ],
                                    selectionIdentity: $selectionIdentity,
                                    dispatcher: function ($operationRun) use ($tenant, $user, $ids, $count): void {
                                        if ($count >= 20) {
                                            BulkPolicyUnignoreJob::dispatch(
                                                tenantId: (int) $tenant->getKey(),
                                                userId: (int) $user->getKey(),
                                                policyIds: $ids,
                                                operationRun: $operationRun,
                                            );

                                            return;
                                        }

                                        BulkPolicyUnignoreJob::dispatchSync(
                                            tenantId: (int) $tenant->getKey(),
                                            userId: (int) $user->getKey(),
                                            policyIds: $ids,
                                            operationRun: $operationRun,
                                        );
                                    },
                                    initiator: $user,
                                    extraContext: [
                                        'policy_count' => $count,
                                    ],
                                    emitQueuedNotification: false,
                                );

                                OpsUxBrowserEvents::dispatchRunEnqueued($livewire);

                                OperationUxPresenter::queuedToast((string) $opRun->type)
                                    ->actions([
                                        Actions\Action::make('view_run')
                                            ->label('View run')
                                            ->url(OperationRunLinks::view($opRun, $tenant)),
                                    ])
                                    ->send();
                            })
                            ->deselectRecordsAfterCompletion(),
                    )
                        ->requireCapability(Capabilities::TENANT_MANAGE)
                        ->apply(),

                    UiEnforcement::forBulkAction(
                        BulkAction::make('bulk_sync')
                            ->label('Sync Policies')
                            ->icon('heroicon-o-arrow-path')
                            ->color('primary')
                            ->requiresConfirmation()
                            ->hidden(function (HasTable $livewire): bool {
                                $visibilityFilterState = $livewire->getTableFilterState('visibility') ?? [];
                                $value = $visibilityFilterState['value'] ?? null;

                                return $value === 'ignored';
                            })
                            ->action(function (Collection $records, HasTable $livewire): void {
                                $tenant = Tenant::current();
                                $user = auth()->user();
                                $count = $records->count();

                                if (! $tenant instanceof Tenant) {
                                    abort(404);
                                }

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

                                $ids = $records
                                    ->pluck('id')
                                    ->map(static fn ($id): int => (int) $id)
                                    ->unique()
                                    ->sort()
                                    ->values()
                                    ->all();

                                /** @var OperationRunService $opService */
                                $opService = app(OperationRunService::class);
                                $opRun = $opService->ensureRun(
                                    tenant: $tenant,
                                    type: 'policy.sync',
                                    inputs: [
                                        'scope' => 'subset',
                                        'policy_ids' => $ids,
                                    ],
                                    initiator: $user
                                );

                                if (! $opRun->wasRecentlyCreated && in_array($opRun->status, ['queued', 'running'], true)) {
                                    OpsUxBrowserEvents::dispatchRunEnqueued($livewire);
                                    OperationUxPresenter::alreadyQueuedToast((string) $opRun->type)
                                        ->actions([
                                            Actions\Action::make('view_run')
                                                ->label('View run')
                                                ->url(OperationRunLinks::view($opRun, $tenant)),
                                        ])
                                        ->send();

                                    return;
                                }

                                SyncPoliciesJob::dispatch((int) $tenant->getKey(), null, $ids, $opRun);
                                OpsUxBrowserEvents::dispatchRunEnqueued($livewire);
                                OperationUxPresenter::queuedToast((string) $opRun->type)
                                    ->actions([
                                        Actions\Action::make('view_run')
                                            ->label('View run')
                                            ->url(OperationRunLinks::view($opRun, $tenant)),
                                    ])
                                    ->send();
                            })
                            ->deselectRecordsAfterCompletion(),
                    )
                        ->requireCapability(Capabilities::TENANT_SYNC)
                        ->apply(),

                    UiEnforcement::forBulkAction(
                        BulkAction::make('bulk_export')
                            ->label('Export to Backup')
                            ->icon('heroicon-o-archive-box-arrow-down')
                            ->form([
                                Forms\Components\TextInput::make('backup_name')
                                    ->label('Backup Name')
                                    ->required()
                                    ->default(fn () => 'Backup '.now()->toDateTimeString()),
                            ])
                            ->action(function (Collection $records, array $data): void {
                                $tenant = Tenant::current();
                                $user = auth()->user();
                                $count = $records->count();
                                $ids = $records->pluck('id')->toArray();

                                if (! $tenant instanceof Tenant) {
                                    abort(404);
                                }

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

                                /** @var BulkSelectionIdentity $selection */
                                $selection = app(BulkSelectionIdentity::class);

                                $selectionIdentity = $selection->fromIds($ids);

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

                                $opRun = $runs->enqueueBulkOperation(
                                    tenant: $tenant,
                                    type: 'policy.export',
                                    targetScope: [
                                        'entra_tenant_id' => (string) ($tenant->tenant_id ?? $tenant->external_id),
                                    ],
                                    selectionIdentity: $selectionIdentity,
                                    dispatcher: function ($operationRun) use ($tenant, $user, $ids, $data, $count): void {
                                        if ($count >= 20) {
                                            BulkPolicyExportJob::dispatch(
                                                tenantId: (int) $tenant->getKey(),
                                                userId: (int) $user->getKey(),
                                                policyIds: $ids,
                                                backupName: (string) $data['backup_name'],
                                                operationRun: $operationRun,
                                            );

                                            return;
                                        }

                                        BulkPolicyExportJob::dispatchSync(
                                            tenantId: (int) $tenant->getKey(),
                                            userId: (int) $user->getKey(),
                                            policyIds: $ids,
                                            backupName: (string) $data['backup_name'],
                                            operationRun: $operationRun,
                                        );
                                    },
                                    initiator: $user,
                                    extraContext: [
                                        'backup_name' => (string) $data['backup_name'],
                                        'policy_count' => $count,
                                    ],
                                    emitQueuedNotification: false,
                                );

                                OperationUxPresenter::queuedToast((string) $opRun->type)
                                    ->actions([
                                        Actions\Action::make('view_run')
                                            ->label('View run')
                                            ->url(OperationRunLinks::view($opRun, $tenant)),
                                    ])
                                    ->send();
                            })
                            ->deselectRecordsAfterCompletion(),
                    )
                        ->requireCapability(Capabilities::TENANT_MANAGE)
                        ->apply(),
                ])->label('More'),
            ])
            ->emptyStateHeading('No policies synced yet')
            ->emptyStateDescription('Sync your first tenant to see Intune policies here.')
            ->emptyStateIcon('heroicon-o-arrow-path')
            ->emptyStateActions([
                static::makeSyncAction(),
            ]);
    }

    public static function getEloquentQuery(): Builder
    {
        $tenantId = Tenant::currentOrFail()->getKey();

        return parent::getEloquentQuery()
            ->when($tenantId, fn (Builder $query) => $query->where('tenant_id', $tenantId))
            ->withCount('versions')
            ->with([
                'versions' => fn ($query) => $query->orderByDesc('captured_at')->limit(1),
            ]);
    }

    public static function getRelations(): array
    {
        return [
            VersionsRelationManager::class,
        ];
    }

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

    private static function latestSnapshot(Policy $record): array
    {
        $snapshot = $record->relationLoaded('versions')
            ? $record->versions->first()?->snapshot
            : $record->versions()->orderByDesc('captured_at')->value('snapshot');

        if (is_string($snapshot)) {
            $decoded = json_decode($snapshot, true);
            $snapshot = $decoded ?? [];
        }

        if (is_array($snapshot)) {
            return $snapshot;
        }

        return [];
    }

    private static function latestVersionMetadata(Policy $record): array
    {
        $metadata = $record->relationLoaded('versions')
            ? $record->versions->first()?->metadata
            : $record->versions()->orderByDesc('captured_at')->value('metadata');

        if (is_string($metadata)) {
            $decoded = json_decode($metadata, true);
            $metadata = $decoded ?? [];
        }

        return is_array($metadata) ? $metadata : [];
    }

    /**
     * @return array<string, mixed>
     */
    private static function normalizedPolicyState(Policy $record): array
    {
        $cacheKey = 'tenantpilot.normalizedPolicyState.'.(string) $record->getKey();
        $request = request();

        if ($request->attributes->has($cacheKey)) {
            $cached = $request->attributes->get($cacheKey);

            if (is_array($cached)) {
                return $cached;
            }
        }

        $snapshot = static::latestSnapshot($record);

        $normalized = app(PolicyNormalizer::class)->normalize(
            $snapshot,
            $record->policy_type,
            $record->platform
        );

        $normalized['context'] = 'policy';
        $normalized['record_id'] = (string) $record->getKey();
        $normalized['policy_type'] = $record->policy_type;

        $request->attributes->set($cacheKey, $normalized);

        return $normalized;
    }

    /**
     * @param  array{settings?: array<int, array<string, mixed>>}  $normalized
     * @return array{normalized: array<string, mixed>, general: ?array<string, mixed>}
     */
    private static function splitGeneralBlock(array $normalized): array
    {
        $general = null;
        $filtered = [];

        foreach ($normalized['settings'] ?? [] as $block) {
            if (! is_array($block)) {
                continue;
            }

            $title = $block['title'] ?? null;

            if (is_string($title) && strtolower($title) === 'general') {
                $general = $block;

                continue;
            }

            $filtered[] = $block;
        }

        $normalized['settings'] = $filtered;

        return [
            'normalized' => $normalized,
            'general' => $general,
        ];
    }

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

        return collect(config('tenantpilot.supported_policy_types', []))
            ->firstWhere('type', $type) ?? [];
    }

    private static function usesTabbedLayout(Policy $record): bool
    {
        return true;
    }

    private static function hasSettingsTable(Policy $record): bool
    {
        $normalized = static::normalizedPolicyState($record);
        $rows = $normalized['settings_table']['rows'] ?? [];

        return is_array($rows) && $rows !== [];
    }

    /**
     * @return array{entries: array<int, array{key: string, value: mixed}>}
     */
    private static function generalOverviewState(Policy $record): array
    {
        $snapshot = static::latestSnapshot($record);
        $entries = [];

        $name = $snapshot['displayName'] ?? $snapshot['name'] ?? $record->display_name;
        if (is_string($name) && $name !== '') {
            $entries[] = ['key' => 'Name', 'value' => $name];
        }

        $platforms = $snapshot['platforms'] ?? $snapshot['platform'] ?? $record->platform;
        if (is_string($platforms) && $platforms !== '') {
            $entries[] = ['key' => 'Platforms', 'value' => $platforms];
        } elseif (is_array($platforms) && $platforms !== []) {
            $entries[] = ['key' => 'Platforms', 'value' => $platforms];
        }

        $technologies = $snapshot['technologies'] ?? null;
        if (is_string($technologies) && $technologies !== '') {
            $entries[] = ['key' => 'Technologies', 'value' => $technologies];
        } elseif (is_array($technologies) && $technologies !== []) {
            $entries[] = ['key' => 'Technologies', 'value' => $technologies];
        }

        if (array_key_exists('templateReference', $snapshot)) {
            $entries[] = ['key' => 'Template Reference', 'value' => $snapshot['templateReference']];
        }

        $settingCount = $snapshot['settingCount']
            ?? $snapshot['settingsCount']
            ?? (isset($snapshot['settings']) && is_array($snapshot['settings']) ? count($snapshot['settings']) : null);

        if (is_int($settingCount) || is_numeric($settingCount)) {
            $entries[] = ['key' => 'Setting Count', 'value' => $settingCount];
        }

        $version = $snapshot['version'] ?? null;
        if (is_string($version) && $version !== '') {
            $entries[] = ['key' => 'Version', 'value' => $version];
        } elseif (is_numeric($version)) {
            $entries[] = ['key' => 'Version', 'value' => $version];
        }

        $lastModified = $snapshot['lastModifiedDateTime'] ?? null;
        if (is_string($lastModified) && $lastModified !== '') {
            $entries[] = ['key' => 'Last Modified', 'value' => $lastModified];
        }

        $createdAt = $snapshot['createdDateTime'] ?? null;
        if (is_string($createdAt) && $createdAt !== '') {
            $entries[] = ['key' => 'Created', 'value' => $createdAt];
        }

        $description = $snapshot['description'] ?? null;
        if (is_string($description) && $description !== '') {
            $entries[] = ['key' => 'Description', 'value' => $description];
        }

        return [
            'entries' => $entries,
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private static function settingsTabState(Policy $record): array
    {
        $normalized = static::normalizedPolicyState($record);
        $rows = $normalized['settings_table']['rows'] ?? [];
        $hasSettingsTable = is_array($rows) && $rows !== [];

        if (in_array($record->policy_type, ['settingsCatalogPolicy', 'endpointSecurityPolicy', 'securityBaselinePolicy'], true) && $hasSettingsTable) {
            $split = static::splitGeneralBlock($normalized);

            return $split['normalized'];
        }

        return $normalized;
    }
}
