<?php

namespace App\Filament\Resources\BackupSetResource\RelationManagers;

use App\Filament\Resources\PolicyResource;
use App\Jobs\RemovePoliciesFromBackupSetJob;
use App\Models\BackupItem;
use App\Models\Tenant;
use App\Models\User;
use App\Services\OperationRunService;
use App\Support\OperationRunLinks;
use App\Support\OpsUx\OperationUxPresenter;
use App\Support\OpsUx\OpsUxBrowserEvents;
use Filament\Actions;
use Filament\Notifications\Notification;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;

class BackupItemsRelationManager extends RelationManager
{
    protected static string $relationship = 'items';

    public ?int $pollUntil = null;

    protected $listeners = [
        'backup-set-policy-picker:close' => 'closeAddPoliciesModal',
    ];

    public function closeAddPoliciesModal(): void
    {
        $this->unmountAction();
        $this->resetTable();

        $this->pollUntil = now()->addSeconds(20)->getTimestamp();
    }

    public function shouldPollTable(): bool
    {
        return $this->pollUntil !== null && now()->getTimestamp() < $this->pollUntil;
    }

    public function table(Table $table): Table
    {
        return $table
            ->modifyQueryUsing(fn (Builder $query) => $query->with('policyVersion'))
            ->poll(fn (): ?string => (filled($this->mountedActions) || ! $this->shouldPollTable()) ? null : '2s')
            ->columns([
                Tables\Columns\TextColumn::make('policy.display_name')
                    ->label('Item')
                    ->sortable()
                    ->searchable()
                    ->getStateUsing(fn (BackupItem $record) => $record->resolvedDisplayName()),
                Tables\Columns\TextColumn::make('policyVersion.version_number')
                    ->label('Version')
                    ->badge()
                    ->default('—')
                    ->getStateUsing(fn (BackupItem $record): ?int => $record->policyVersion?->version_number),
                Tables\Columns\TextColumn::make('policy_type')
                    ->label('Type')
                    ->badge()
                    ->formatStateUsing(fn (?string $state) => static::typeMeta($state)['label'] ?? $state),
                Tables\Columns\TextColumn::make('restore_mode')
                    ->label('Restore')
                    ->badge()
                    ->state(fn (BackupItem $record) => static::typeMeta($record->policy_type)['restore'] ?? 'enabled')
                    ->color(fn (?string $state) => $state === 'preview-only' ? 'warning' : 'success'),
                Tables\Columns\TextColumn::make('risk')
                    ->label('Risk')
                    ->badge()
                    ->state(fn (BackupItem $record) => static::typeMeta($record->policy_type)['risk'] ?? 'n/a')
                    ->color(fn (?string $state) => str_contains((string) $state, 'high') ? 'danger' : 'gray'),
                Tables\Columns\TextColumn::make('policy_identifier')
                    ->label('Policy ID')
                    ->copyable(),
                Tables\Columns\TextColumn::make('platform')->badge(),
                Tables\Columns\TextColumn::make('assignments')
                    ->label('Assignments')
                    ->badge()
                    ->color('info')
                    ->getStateUsing(function (BackupItem $record): string {
                        $assignments = $record->policyVersion?->assignments ?? $record->assignments;

                        if (is_array($assignments)) {
                            return (string) count($assignments);
                        }

                        $assignmentsFetched = $record->policyVersion?->metadata['assignments_fetched']
                            ?? $record->metadata['assignments_fetched']
                            ?? false;

                        return $assignmentsFetched ? '0' : '—';
                    }),
                Tables\Columns\TextColumn::make('scope_tags')
                    ->label('Scope Tags')
                    ->default('—')
                    ->getStateUsing(function (BackupItem $record): array {
                        $tags = $record->policyVersion?->scope_tags['names']
                            ?? $record->metadata['scope_tag_names']
                            ?? [];

                        return is_array($tags) ? $tags : [];
                    })
                    ->formatStateUsing(function ($state): string {
                        if (is_array($state)) {
                            return $state === [] ? '—' : implode(', ', $state);
                        }

                        if (is_string($state) && $state !== '') {
                            return $state;
                        }

                        return '—';
                    }),
                Tables\Columns\TextColumn::make('captured_at')->dateTime(),
                Tables\Columns\TextColumn::make('created_at')->since(),
            ])
            ->filters([])
            ->headerActions([
                Actions\Action::make('addPolicies')
                    ->label('Add Policies')
                    ->icon('heroicon-o-plus')
                    ->modalHeading('Add Policies')
                    ->modalSubmitAction(false)
                    ->modalCancelActionLabel('Close')
                    ->modalContent(function (): View {
                        $backupSet = $this->getOwnerRecord();

                        return view('filament.modals.backup-set-policy-picker', [
                            'backupSetId' => $backupSet->getKey(),
                        ]);
                    }),
            ])
            ->actions([
                Actions\ActionGroup::make([
                    Actions\ViewAction::make()
                        ->label('View policy')
                        ->url(function (BackupItem $record): ?string {
                            if (! $record->policy_id) {
                                return null;
                            }

                            $tenant = $this->getOwnerRecord()->tenant ?? \App\Models\Tenant::current();

                            return PolicyResource::getUrl('view', ['record' => $record->policy_id], tenant: $tenant);
                        })
                        ->hidden(fn (BackupItem $record) => ! $record->policy_id)
                        ->openUrlInNewTab(true),
                    Actions\Action::make('remove')
                        ->label('Remove')
                        ->color('danger')
                        ->icon('heroicon-o-x-mark')
                        ->requiresConfirmation()
                        ->action(function (BackupItem $record): void {
                            $backupSet = $this->getOwnerRecord();

                            $user = auth()->user();
                            if (! $user instanceof User) {
                                abort(403);
                            }

                            $tenant = $backupSet->tenant ?? Tenant::current();

                            if (! $user->canSyncTenant($tenant)) {
                                abort(403);
                            }

                            if ((int) $tenant->getKey() !== (int) $backupSet->tenant_id) {
                                abort(403);
                            }

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

                            /** @var OperationRunService $opService */
                            $opService = app(OperationRunService::class);
                            $opRun = $opService->ensureRun(
                                tenant: $tenant,
                                type: 'backup_set.remove_policies',
                                inputs: [
                                    'backup_set_id' => (int) $backupSet->getKey(),
                                    'backup_item_ids' => $backupItemIds,
                                ],
                                initiator: $user,
                            );

                            if (! $opRun->wasRecentlyCreated && in_array($opRun->status, ['queued', 'running'], true)) {
                                Notification::make()
                                    ->title('Removal already queued')
                                    ->body('A matching remove operation is already queued or running.')
                                    ->info()
                                    ->actions([
                                        Actions\Action::make('view_run')
                                            ->label('View run')
                                            ->url(OperationRunLinks::view($opRun, $tenant)),
                                    ])
                                    ->send();

                                return;
                            }

                            $opService->dispatchOrFail($opRun, function () use ($backupSet, $backupItemIds, $user, $opRun): void {
                                RemovePoliciesFromBackupSetJob::dispatch(
                                    backupSetId: (int) $backupSet->getKey(),
                                    backupItemIds: $backupItemIds,
                                    initiatorUserId: (int) $user->getKey(),
                                    operationRun: $opRun,
                                );
                            });

                            $this->dispatch(OpsUxBrowserEvents::RunEnqueued);
                            OperationUxPresenter::queuedToast((string) $opRun->type)
                                ->actions([
                                    Actions\Action::make('view_run')
                                        ->label('View run')
                                        ->url(OperationRunLinks::view($opRun, $tenant)),
                                ])
                                ->send();

                            $this->resetTable();

                            $this->pollUntil = now()->addSeconds(20)->getTimestamp();
                        }),
                ])->icon('heroicon-o-ellipsis-vertical'),
            ])
            ->bulkActions([
                Actions\BulkActionGroup::make([
                    Actions\BulkAction::make('bulk_remove')
                        ->label('Remove selected')
                        ->icon('heroicon-o-x-mark')
                        ->color('danger')
                        ->requiresConfirmation()
                        ->deselectRecordsAfterCompletion()
                        ->action(function (Collection $records): void {
                            if ($records->isEmpty()) {
                                return;
                            }

                            $backupSet = $this->getOwnerRecord();

                            $user = auth()->user();
                            if (! $user instanceof User) {
                                abort(403);
                            }

                            $tenant = $backupSet->tenant ?? Tenant::current();

                            if (! $user->canSyncTenant($tenant)) {
                                abort(403);
                            }

                            if ((int) $tenant->getKey() !== (int) $backupSet->tenant_id) {
                                abort(403);
                            }

                            $backupItemIds = $records
                                ->pluck('id')
                                ->map(fn (mixed $value): int => (int) $value)
                                ->filter(fn (int $value): bool => $value > 0)
                                ->unique()
                                ->sort()
                                ->values()
                                ->all();

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

                            /** @var OperationRunService $opService */
                            $opService = app(OperationRunService::class);
                            $opRun = $opService->ensureRun(
                                tenant: $tenant,
                                type: 'backup_set.remove_policies',
                                inputs: [
                                    'backup_set_id' => (int) $backupSet->getKey(),
                                    'backup_item_ids' => $backupItemIds,
                                ],
                                initiator: $user,
                            );

                            if (! $opRun->wasRecentlyCreated && in_array($opRun->status, ['queued', 'running'], true)) {
                                Notification::make()
                                    ->title('Removal already queued')
                                    ->body('A matching remove operation is already queued or running.')
                                    ->info()
                                    ->actions([
                                        Actions\Action::make('view_run')
                                            ->label('View run')
                                            ->url(OperationRunLinks::view($opRun, $tenant)),
                                    ])
                                    ->send();

                                return;
                            }

                            $opService->dispatchOrFail($opRun, function () use ($backupSet, $backupItemIds, $user, $opRun): void {
                                RemovePoliciesFromBackupSetJob::dispatch(
                                    backupSetId: (int) $backupSet->getKey(),
                                    backupItemIds: $backupItemIds,
                                    initiatorUserId: (int) $user->getKey(),
                                    operationRun: $opRun,
                                );
                            });

                            $this->dispatch(OpsUxBrowserEvents::RunEnqueued);
                            Notification::make()
                                ->title('Removal queued')
                                ->body('A background job has been queued. You can monitor progress in the run details or progress widget.')
                                ->success()
                                ->actions([
                                    Actions\Action::make('view_run')
                                        ->label('View run')
                                        ->url(OperationRunLinks::view($opRun, $tenant)),
                                ])
                                ->sendToDatabase($user)
                                ->send();

                            $this->resetTable();

                            $this->pollUntil = now()->addSeconds(20)->getTimestamp();
                        }),
                ]),
            ]);
    }

    /**
     * @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) ?? [];
    }
}
