<?php

namespace App\Filament\Resources;

use App\Filament\Resources\BulkOperationRunResource\Pages;
use App\Models\BulkOperationRun;
use App\Models\Tenant;
use App\Support\OperationRunLinks;
use BackedEnum;
use Filament\Actions;
use Filament\Forms\Components\DatePicker;
use Filament\Infolists\Components\TextEntry;
use Filament\Infolists\Components\ViewEntry;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use UnitEnum;

class BulkOperationRunResource extends Resource
{
    protected static bool $isScopedToTenant = false;

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

    protected static bool $shouldRegisterNavigation = false;

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

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

    protected static ?string $navigationLabel = 'Operations';

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

    public static function infolist(Schema $schema): Schema
    {
        return $schema
            ->schema([
                Section::make('Legacy run view')
                    ->description('Canonical monitoring is now available in Monitoring → Operations.')
                    ->schema([
                        TextEntry::make('canonical_view')
                            ->label('Canonical view')
                            ->state('View in Operations')
                            ->url(fn (BulkOperationRun $record): string => OperationRunLinks::index(Tenant::current() ?? $record->tenant))
                            ->badge()
                            ->color('primary'),
                    ])
                    ->columnSpanFull(),

                Section::make('Run')
                    ->schema([
                        TextEntry::make('user.name')
                            ->label('Initiator')
                            ->placeholder('—'),
                        TextEntry::make('resource')->badge(),
                        TextEntry::make('action')->badge(),
                        TextEntry::make('status')
                            ->label('Outcome')
                            ->badge()
                            ->state(fn (BulkOperationRun $record): string => $record->statusBucket())
                            ->color(fn (BulkOperationRun $record): string => static::statusBucketColor($record->statusBucket())),
                        TextEntry::make('total_items')->label('Total')->numeric(),
                        TextEntry::make('processed_items')->label('Processed')->numeric(),
                        TextEntry::make('succeeded')->numeric(),
                        TextEntry::make('failed')->numeric(),
                        TextEntry::make('skipped')->numeric(),
                        TextEntry::make('created_at')->dateTime(),
                        TextEntry::make('updated_at')->dateTime(),
                        TextEntry::make('idempotency_key')->label('Idempotency key')->copyable()->placeholder('—'),
                    ])
                    ->columns(2)
                    ->columnSpanFull(),

                Section::make('Related')
                    ->schema([
                        TextEntry::make('related_backup_set')
                            ->label('Backup set')
                            ->state(function (BulkOperationRun $record): ?string {
                                $backupSetId = static::backupSetIdFromItemIds($record);

                                if (! $backupSetId) {
                                    return null;
                                }

                                return "#{$backupSetId}";
                            })
                            ->url(function (BulkOperationRun $record): ?string {
                                $backupSetId = static::backupSetIdFromItemIds($record);

                                if (! $backupSetId) {
                                    return null;
                                }

                                return BackupSetResource::getUrl('view', ['record' => $backupSetId], tenant: Tenant::current());
                            })
                            ->visible(fn (BulkOperationRun $record): bool => static::backupSetIdFromItemIds($record) !== null)
                            ->placeholder('—')
                            ->columnSpanFull(),
                        TextEntry::make('related_drift_findings')
                            ->label('Drift findings')
                            ->state('View')
                            ->url(function (BulkOperationRun $record): ?string {
                                if ($record->runType() !== 'drift.generate') {
                                    return null;
                                }

                                $payload = $record->item_ids ?? [];
                                if (! is_array($payload)) {
                                    return FindingResource::getUrl('index', tenant: Tenant::current());
                                }

                                $scopeKey = null;
                                $baselineRunId = null;
                                $currentRunId = null;

                                if (array_is_list($payload) && isset($payload[0]) && is_string($payload[0])) {
                                    $scopeKey = $payload[0];
                                } else {
                                    $scopeKey = is_string($payload['scope_key'] ?? null) ? $payload['scope_key'] : null;

                                    if (is_numeric($payload['baseline_run_id'] ?? null)) {
                                        $baselineRunId = (int) $payload['baseline_run_id'];
                                    }

                                    if (is_numeric($payload['current_run_id'] ?? null)) {
                                        $currentRunId = (int) $payload['current_run_id'];
                                    }
                                }

                                $tableFilters = [];

                                if (is_string($scopeKey) && $scopeKey !== '') {
                                    $tableFilters['scope_key'] = ['scope_key' => $scopeKey];
                                }

                                if (is_int($baselineRunId) || is_int($currentRunId)) {
                                    $tableFilters['run_ids'] = [
                                        'baseline_run_id' => $baselineRunId,
                                        'current_run_id' => $currentRunId,
                                    ];
                                }

                                $parameters = $tableFilters !== [] ? ['tableFilters' => $tableFilters] : [];

                                return FindingResource::getUrl('index', $parameters, tenant: Tenant::current());
                            })
                            ->visible(fn (BulkOperationRun $record): bool => $record->runType() === 'drift.generate')
                            ->placeholder('—')
                            ->columnSpanFull(),
                    ])
                    ->visible(fn (BulkOperationRun $record): bool => in_array($record->runType(), ['backup_set.add_policies', 'drift.generate'], true))
                    ->columnSpanFull(),

                Section::make('Items')
                    ->schema([
                        ViewEntry::make('item_ids')
                            ->label('')
                            ->view('filament.infolists.entries.snapshot-json')
                            ->state(fn (BulkOperationRun $record) => $record->item_ids ?? [])
                            ->columnSpanFull(),
                    ])
                    ->columnSpanFull(),

                Section::make('Failures')
                    ->schema([
                        ViewEntry::make('failures')
                            ->label('')
                            ->view('filament.infolists.entries.snapshot-json')
                            ->state(fn (BulkOperationRun $record) => $record->failures ?? [])
                            ->columnSpanFull(),
                    ])
                    ->columnSpanFull(),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->defaultSort('id', 'desc')
            ->modifyQueryUsing(function (Builder $query): Builder {
                $tenantId = Tenant::current()->getKey();

                return $query->when($tenantId, fn (Builder $q) => $q->where('tenant_id', $tenantId));
            })
            ->columns([
                Tables\Columns\TextColumn::make('user.name')
                    ->label('Initiator')
                    ->placeholder('—')
                    ->toggleable(),
                Tables\Columns\TextColumn::make('resource')->badge(),
                Tables\Columns\TextColumn::make('action')->badge(),
                Tables\Columns\TextColumn::make('status')
                    ->label('Outcome')
                    ->badge()
                    ->formatStateUsing(fn (BulkOperationRun $record): string => $record->statusBucket())
                    ->color(fn (BulkOperationRun $record): string => static::statusBucketColor($record->statusBucket())),
                Tables\Columns\TextColumn::make('created_at')->since(),
                Tables\Columns\TextColumn::make('total_items')->label('Total')->numeric(),
                Tables\Columns\TextColumn::make('processed_items')->label('Processed')->numeric(),
                Tables\Columns\TextColumn::make('failed')->numeric(),
            ])
            ->filters([
                Tables\Filters\SelectFilter::make('run_type')
                    ->label('Run type')
                    ->options(fn (): array => static::runTypeOptions())
                    ->query(function (Builder $query, array $data): Builder {
                        $value = $data['value'] ?? null;

                        if (! is_string($value) || $value === '' || ! str_contains($value, '.')) {
                            return $query;
                        }

                        [$resource, $action] = explode('.', $value, 2);

                        if ($resource === '' || $action === '') {
                            return $query;
                        }

                        return $query
                            ->where('resource', $resource)
                            ->where('action', $action);
                    }),
                Tables\Filters\SelectFilter::make('status_bucket')
                    ->label('Status')
                    ->options([
                        'queued' => 'Queued',
                        'running' => 'Running',
                        'succeeded' => 'Succeeded',
                        'partially succeeded' => 'Partially succeeded',
                        'failed' => 'Failed',
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        $value = $data['value'] ?? null;

                        if (! is_string($value) || $value === '') {
                            return $query;
                        }

                        $nonSkippedFailureSql = "EXISTS (SELECT 1 FROM jsonb_array_elements(COALESCE(failures, '[]'::jsonb)) AS elem WHERE (elem->>'type' IS NULL OR elem->>'type' <> 'skipped'))";

                        return match ($value) {
                            'queued' => $query->where('status', 'pending'),
                            'running' => $query->where('status', 'running'),
                            'succeeded' => $query
                                ->whereIn('status', ['completed', 'completed_with_errors'])
                                ->where('failed', 0)
                                ->whereRaw("NOT {$nonSkippedFailureSql}"),
                            'partially succeeded' => $query
                                ->whereNotIn('status', ['pending', 'running'])
                                ->where('succeeded', '>', 0)
                                ->where(function (Builder $q) use ($nonSkippedFailureSql): void {
                                    $q->where('failed', '>', 0)->orWhereRaw($nonSkippedFailureSql);
                                }),
                            'failed' => $query
                                ->whereNotIn('status', ['pending', 'running'])
                                ->where(function (Builder $q) use ($nonSkippedFailureSql): void {
                                    $q->where(function (Builder $q) use ($nonSkippedFailureSql): void {
                                        $q->where('succeeded', 0)
                                            ->where(function (Builder $q) use ($nonSkippedFailureSql): void {
                                                $q->where('failed', '>', 0)->orWhereRaw($nonSkippedFailureSql);
                                            });
                                    })->orWhere(function (Builder $q) use ($nonSkippedFailureSql): void {
                                        $q->whereIn('status', ['failed', 'aborted'])
                                            ->whereNot(function (Builder $q) use ($nonSkippedFailureSql): void {
                                                $q->where('succeeded', '>', 0)
                                                    ->where(function (Builder $q) use ($nonSkippedFailureSql): void {
                                                        $q->where('failed', '>', 0)->orWhereRaw($nonSkippedFailureSql);
                                                    });
                                            });
                                    });
                                }),
                            default => $query,
                        };
                    }),
                Tables\Filters\Filter::make('created_at')
                    ->label('Created')
                    ->form([
                        DatePicker::make('created_from')
                            ->label('From')
                            ->default(fn () => now()->subDays(30)),
                        DatePicker::make('created_until')
                            ->label('Until')
                            ->default(fn () => now()),
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        $from = $data['created_from'] ?? null;
                        if ($from) {
                            $query->whereDate('created_at', '>=', $from);
                        }

                        $until = $data['created_until'] ?? null;
                        if ($until) {
                            $query->whereDate('created_at', '<=', $until);
                        }

                        return $query;
                    }),
            ])
            ->actions([
                Actions\ViewAction::make(),
            ])
            ->bulkActions([]);
    }

    public static function getEloquentQuery(): Builder
    {
        return parent::getEloquentQuery()
            ->with('user')
            ->latest('id');
    }

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

    /**
     * @return array<string, string>
     */
    private static function runTypeOptions(): array
    {
        $tenantId = Tenant::current()->getKey();

        $knownTypes = [
            'drift.generate' => 'drift.generate',
            'backup_set.add_policies' => 'backup_set.add_policies',
        ];

        $storedTypes = BulkOperationRun::query()
            ->where('tenant_id', $tenantId)
            ->select(['resource', 'action'])
            ->distinct()
            ->orderBy('resource')
            ->orderBy('action')
            ->get()
            ->mapWithKeys(function (BulkOperationRun $run): array {
                $type = "{$run->resource}.{$run->action}";

                return [$type => $type];
            })
            ->all();

        return array_replace($storedTypes, $knownTypes);
    }

    private static function statusBucketColor(string $statusBucket): string
    {
        return match ($statusBucket) {
            'succeeded' => 'success',
            'partially succeeded' => 'warning',
            'failed' => 'danger',
            'running' => 'info',
            'queued' => 'gray',
            default => 'gray',
        };
    }

    private static function backupSetIdFromItemIds(BulkOperationRun $record): ?int
    {
        if ($record->runType() !== 'backup_set.add_policies') {
            return null;
        }

        $payload = $record->item_ids ?? [];
        if (! is_array($payload)) {
            return null;
        }

        $backupSetId = $payload['backup_set_id'] ?? null;
        if (! is_numeric($backupSetId)) {
            return null;
        }

        $backupSetId = (int) $backupSetId;

        return $backupSetId > 0 ? $backupSetId : null;
    }
}
