<?php

namespace App\Filament\Resources\FindingResource\Pages;

use App\Filament\Resources\FindingResource;
use App\Filament\Widgets\Tenant\BaselineCompareCoverageBanner;
use App\Jobs\BackfillFindingLifecycleJob;
use App\Models\Finding;
use App\Models\Tenant;
use App\Models\User;
use App\Services\Findings\FindingWorkflowService;
use App\Services\OperationRunService;
use App\Support\Auth\Capabilities;
use App\Support\OperationRunLinks;
use App\Support\OpsUx\OperationUxPresenter;
use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\Rbac\UiEnforcement;
use App\Support\Rbac\UiTooltips;
use Filament\Actions;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ListRecords;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Throwable;

class ListFindings extends ListRecords
{
    protected static string $resource = FindingResource::class;

    protected function getHeaderWidgets(): array
    {
        return [
            BaselineCompareCoverageBanner::class,
        ];
    }

    protected function getHeaderActions(): array
    {
        $actions = [];

        if ((bool) config('tenantpilot.allow_admin_maintenance_actions', false)) {
            $actions[] = UiEnforcement::forAction(
                Actions\Action::make('backfill_lifecycle')
                    ->label('Backfill findings lifecycle')
                    ->icon('heroicon-o-wrench-screwdriver')
                    ->color('gray')
                    ->requiresConfirmation()
                    ->modalHeading('Backfill findings lifecycle')
                    ->modalDescription('This will backfill legacy Findings data (lifecycle fields, SLA due dates, and drift duplicate consolidation) for the current tenant. The operation runs in the background.')
                    ->action(function (OperationRunService $operationRuns): void {
                        $user = auth()->user();

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

                        $tenant = \Filament\Facades\Filament::getTenant();

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

                        $opRun = $operationRuns->ensureRunWithIdentity(
                            tenant: $tenant,
                            type: 'findings.lifecycle.backfill',
                            identityInputs: [
                                'tenant_id' => (int) $tenant->getKey(),
                                'trigger' => 'backfill',
                            ],
                            context: [
                                'workspace_id' => (int) $tenant->workspace_id,
                                'initiator_user_id' => (int) $user->getKey(),
                            ],
                            initiator: $user,
                        );

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

                        if ($opRun->wasRecentlyCreated === false) {
                            OpsUxBrowserEvents::dispatchRunEnqueued($this);

                            OperationUxPresenter::alreadyQueuedToast((string) $opRun->type)
                                ->actions([
                                    Actions\Action::make('view_run')
                                        ->label('View run')
                                        ->url($runUrl),
                                ])
                                ->send();

                            return;
                        }

                        $operationRuns->dispatchOrFail($opRun, function () use ($tenant, $user): void {
                            BackfillFindingLifecycleJob::dispatch(
                                tenantId: (int) $tenant->getKey(),
                                workspaceId: (int) $tenant->workspace_id,
                                initiatorUserId: (int) $user->getKey(),
                            );
                        });

                        OpsUxBrowserEvents::dispatchRunEnqueued($this);

                        OperationUxPresenter::queuedToast((string) $opRun->type)
                            ->body('The backfill will run in the background. You can continue working while it completes.')
                            ->actions([
                                Actions\Action::make('view_run')
                                    ->label('View run')
                                    ->url($runUrl),
                            ])
                            ->send();
                    })
            )
                ->preserveVisibility()
                ->requireCapability(Capabilities::TENANT_MANAGE)
                ->tooltip(UiTooltips::INSUFFICIENT_PERMISSION)
                ->apply();
        }

        $actions[] = UiEnforcement::forAction(
            Actions\Action::make('triage_all_matching')
                ->label('Triage all matching')
                ->icon('heroicon-o-check')
                ->color('gray')
                ->requiresConfirmation()
                ->visible(fn (): bool => $this->getStatusFilterValue() === Finding::STATUS_NEW)
                ->modalDescription(function (): string {
                    $count = $this->getAllMatchingCount();

                    return "You are about to triage {$count} finding".($count === 1 ? '' : 's').' matching the current filters.';
                })
                ->form(function (): array {
                    $count = $this->getAllMatchingCount();

                    if ($count <= 100) {
                        return [];
                    }

                    return [
                        TextInput::make('confirmation')
                            ->label('Type TRIAGE to confirm')
                            ->required()
                            ->in(['TRIAGE'])
                            ->validationMessages([
                                'in' => 'Please type TRIAGE to confirm.',
                            ]),
                    ];
                })
                ->action(function (FindingWorkflowService $workflow): void {
                    $query = $this->buildAllMatchingQuery();
                    $count = (clone $query)->count();

                    if ($count === 0) {
                        Notification::make()
                            ->title('No matching findings')
                            ->body('There are no new findings matching the current filters to triage.')
                            ->warning()
                            ->send();

                        return;
                    }

                    $user = auth()->user();
                    $tenant = \Filament\Facades\Filament::getTenant();

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

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

                    $triagedCount = 0;
                    $skippedCount = 0;
                    $failedCount = 0;

                    $query->orderBy('id')->chunkById(200, function ($findings) use ($workflow, $tenant, $user, &$triagedCount, &$skippedCount, &$failedCount): void {
                        foreach ($findings as $finding) {
                            if (! $finding instanceof Finding) {
                                $skippedCount++;

                                continue;
                            }

                            if (! in_array((string) $finding->status, [
                                Finding::STATUS_NEW,
                                Finding::STATUS_REOPENED,
                                Finding::STATUS_ACKNOWLEDGED,
                            ], true)) {
                                $skippedCount++;

                                continue;
                            }

                            try {
                                $workflow->triage($finding, $tenant, $user);
                                $triagedCount++;
                            } catch (Throwable) {
                                $failedCount++;
                            }
                        }
                    });

                    $this->deselectAllTableRecords();
                    $this->resetPage();

                    $body = "Triaged {$triagedCount} finding".($triagedCount === 1 ? '' : 's').'.';
                    if ($skippedCount > 0) {
                        $body .= " Skipped {$skippedCount}.";
                    }
                    if ($failedCount > 0) {
                        $body .= " Failed {$failedCount}.";
                    }

                    Notification::make()
                        ->title('Bulk triage completed')
                        ->body($body)
                        ->status($failedCount > 0 ? 'warning' : 'success')
                        ->send();
                })
        )
            ->preserveVisibility()
            ->requireCapability(Capabilities::TENANT_FINDINGS_TRIAGE)
            ->tooltip(UiTooltips::INSUFFICIENT_PERMISSION)
            ->apply();

        return $actions;
    }

    protected function buildAllMatchingQuery(): Builder
    {
        $query = Finding::query();

        $tenantId = \Filament\Facades\Filament::getTenant()?->getKey();

        if (! is_numeric($tenantId)) {
            return $query->whereRaw('1 = 0');
        }

        $query->where('tenant_id', (int) $tenantId);

        $query->where('status', Finding::STATUS_NEW);

        $findingType = $this->getFindingTypeFilterValue();
        if (is_string($findingType) && $findingType !== '') {
            $query->where('finding_type', $findingType);
        }

        if ($this->filterIsActive('overdue')) {
            $query->whereNotNull('due_at')->where('due_at', '<', now());
        }

        if ($this->filterIsActive('high_severity')) {
            $query->whereIn('severity', [
                Finding::SEVERITY_HIGH,
                Finding::SEVERITY_CRITICAL,
            ]);
        }

        if ($this->filterIsActive('my_assigned')) {
            $userId = auth()->id();

            if (is_numeric($userId)) {
                $query->where('assignee_user_id', (int) $userId);
            } else {
                $query->whereRaw('1 = 0');
            }
        }

        $scopeKeyState = $this->getTableFilterState('scope_key') ?? [];
        $scopeKey = Arr::get($scopeKeyState, 'scope_key');
        if (is_string($scopeKey) && $scopeKey !== '') {
            $query->where('scope_key', $scopeKey);
        }

        $runIdsState = $this->getTableFilterState('run_ids') ?? [];
        $baselineRunId = Arr::get($runIdsState, 'baseline_operation_run_id');
        if (is_numeric($baselineRunId)) {
            $query->where('baseline_operation_run_id', (int) $baselineRunId);
        }

        $currentRunId = Arr::get($runIdsState, 'current_operation_run_id');
        if (is_numeric($currentRunId)) {
            $query->where('current_operation_run_id', (int) $currentRunId);
        }

        return $query;
    }

    private function filterIsActive(string $filterName): bool
    {
        $state = $this->getTableFilterState($filterName);

        if ($state === true) {
            return true;
        }

        if (is_array($state)) {
            $isActive = Arr::get($state, 'isActive');

            return $isActive === true;
        }

        return false;
    }

    protected function getAllMatchingCount(): int
    {
        return (int) $this->buildAllMatchingQuery()->count();
    }

    protected function getStatusFilterValue(): string
    {
        $state = $this->getTableFilterState('status') ?? [];
        $value = Arr::get($state, 'value');

        return is_string($value) && $value !== ''
            ? $value
            : Finding::STATUS_NEW;
    }

    protected function getFindingTypeFilterValue(): ?string
    {
        $state = $this->getTableFilterState('finding_type') ?? [];
        $value = Arr::get($state, 'value');

        return is_string($value) && $value !== ''
            ? $value
            : null;
    }
}
