<?php

declare(strict_types=1);

namespace App\Filament\Pages\Monitoring;

use App\Filament\Concerns\CleansAdminTenantQueryParameter;
use App\Filament\Concerns\ClearsWorkspaceHubEnvironmentFilterState;
use App\Filament\Resources\FindingExceptionResource;
use App\Filament\Resources\FindingResource;
use App\Models\FindingException;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Services\Findings\FindingExceptionService;
use App\Services\Findings\FindingRiskGovernanceResolver;
use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use App\Support\Filament\FilterOptionCatalog;
use App\Support\Filament\TablePaginationProfiles;
use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\Navigation\WorkspaceHubEnvironmentFilter;
use App\Support\OperateHub\OperateHubShell;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\ActionSurfaceDefaults;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType;
use App\Support\Ui\GovernanceActions\GovernanceActionCatalog;
use App\Support\Workspaces\WorkspaceContext;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Filament\Facades\Filament;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\Textarea;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use UnitEnum;

class FindingExceptionsQueue extends Page implements HasTable
{
    use CleansAdminTenantQueryParameter;
    use ClearsWorkspaceHubEnvironmentFilterState;
    use InteractsWithTable;

    protected const MONITORING_PAGE_STATE_CONTRACT = [
        'surfaceKey' => 'finding_exceptions_queue',
        'surfaceType' => 'selected_record_monitoring',
        'stateFields' => [
            [
                'stateKey' => 'exception',
                'stateClass' => 'inspect',
                'carrier' => 'query_param',
                'queryRole' => 'durable_restorable',
                'shareable' => true,
                'restorableOnRefresh' => true,
                'tenantSensitive' => false,
                'invalidFallback' => 'clear_selection_and_continue',
            ],
            [
                'stateKey' => 'environment_id',
                'stateClass' => 'contextual_prefilter',
                'carrier' => 'query_param',
                'queryRole' => 'durable_restorable',
                'shareable' => true,
                'restorableOnRefresh' => true,
                'tenantSensitive' => true,
                'invalidFallback' => 'discard_and_continue',
            ],
            [
                'stateKey' => 'tableFilters',
                'stateClass' => 'shareable_restorable',
                'carrier' => 'session',
                'queryRole' => 'unsupported',
                'shareable' => false,
                'restorableOnRefresh' => true,
                'tenantSensitive' => true,
                'invalidFallback' => 'discard_and_continue',
            ],
            [
                'stateKey' => 'tableSearch',
                'stateClass' => 'shareable_restorable',
                'carrier' => 'session',
                'queryRole' => 'unsupported',
                'shareable' => false,
                'restorableOnRefresh' => true,
                'tenantSensitive' => false,
                'invalidFallback' => 'discard_and_continue',
            ],
        ],
        'hydrationRule' => [
            'precedenceOrder' => ['query', 'session', 'default'],
            'appliesOnInitialMountOnly' => true,
            'activeStateBecomesAuthoritativeAfterMount' => true,
            'clearsOnTenantSwitch' => ['environment_id', 'managed_environment_id', 'status', 'current_validity_state'],
            'invalidRequestedStateFallback' => 'clear_selection_and_continue',
        ],
        'inspectContract' => [
            'primaryModel' => FindingException::class,
            'selectedStateKey' => 'selectedFindingExceptionId',
            'openedBy' => ['query_param', 'inspect_action'],
            'presentation' => 'summary_plus_related_actions',
            'shareable' => true,
            'invalidSelectionFallback' => 'clear_selection_and_continue',
        ],
        'shareableStateKeys' => ['environment_id', 'exception'],
        'localOnlyStateKeys' => [],
    ];

    public ?int $selectedFindingExceptionId = null;

    protected static bool $isDiscovered = false;

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

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

    protected static ?string $navigationLabel = 'Finding exceptions';

    protected static ?string $slug = 'finding-exceptions/queue';

    protected static ?string $title = 'Finding Exceptions Queue';

    protected string $view = 'filament.pages.monitoring.finding-exceptions-queue';

    /**
     * @var array<int, ManagedEnvironment>|null
     */
    private ?array $authorizedTenants = null;

    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::RunLog, ActionSurfaceType::QueueReview)
            ->withDefaults(new ActionSurfaceDefaults(
                moreGroupLabel: 'More',
                exportIsDefaultBulkActionForReadOnly: false,
            ))
            ->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions keep workspace approval scope visible and expose selected exception review actions.')
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ViewAction->value)
            ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'Exception decisions are reviewed one record at a time in v1 and do not expose bulk actions.')
            ->satisfy(ActionSurfaceSlot::ListEmptyState, 'Empty state explains when the approval queue is empty and keeps navigation back to environment findings available.')
            ->satisfy(ActionSurfaceSlot::DetailHeader, 'Selected exception detail exposes approve, reject, and related-record navigation actions in the page header.');
    }

    /**
     * @return array<string, mixed>
     */
    public static function monitoringPageStateContract(): array
    {
        return self::MONITORING_PAGE_STATE_CONTRACT;
    }

    public static function canAccess(): bool
    {
        if (Filament::getCurrentPanel()?->getId() !== 'admin') {
            return false;
        }

        $user = auth()->user();

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

        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());

        if (! is_int($workspaceId)) {
            return false;
        }

        $workspace = Workspace::query()->whereKey($workspaceId)->first();

        if (! $workspace instanceof Workspace) {
            return false;
        }

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

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

    public function mount(): void
    {
        $this->resetWorkspaceHubEnvironmentFilterStateForCleanEntry(request());

        $this->mountInteractsWithTable();
        $this->resetWorkspaceHubEnvironmentFilterStateForCleanEntry(request());
        $this->applyRequestedTenantPrefilter();
        $requestedExceptionId = is_numeric(request()->query('exception')) ? (int) request()->query('exception') : null;

        if ($requestedExceptionId !== null) {
            $this->selectedFindingExceptionId = $this->resolveSelectedFindingExceptionId($requestedExceptionId);
        }
    }

    protected function getHeaderActions(): array
    {
        $actions = app(OperateHubShell::class)->headerActions(
            scopeActionName: 'operate_hub_scope_finding_exceptions',
            returnActionName: 'operate_hub_return_finding_exceptions',
        );

        $governanceContext = $this->incomingGovernanceContext();

        if ($governanceContext?->backLinkUrl !== null) {
            $actions[] = Action::make('return_to_governance_inbox')
                ->label($governanceContext->backLinkLabel ?? 'Back to governance inbox')
                ->icon('heroicon-o-arrow-left')
                ->color('gray')
                ->url($governanceContext->backLinkUrl);
        }

        $actions[] = Action::make('clear_filters')
            ->label('Clear filters')
            ->icon('heroicon-o-x-mark')
            ->color('gray')
            ->visible(fn (): bool => $this->hasActiveQueueFilters())
            ->action(fn (): mixed => $this->clearQueueFilters());

        $actions[] = Action::make('view_tenant_register')
            ->label('View environment findings')
            ->icon('heroicon-o-arrow-top-right-on-square')
            ->color('gray')
            ->visible(fn (): bool => $this->filteredTenant() instanceof ManagedEnvironment)
            ->url(function (): ?string {
                $tenant = $this->filteredTenant();

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

                return FindingExceptionResource::getUrl('index', tenant: $tenant);
            });

        $selectedContextActions = [
            Action::make('clear_selected_exception')
                ->label('Close details')
                ->color('gray')
                ->visible(fn (): bool => $this->selectedFindingException() instanceof FindingException)
                ->url(fn (): string => $this->queueUrl(['exception' => null])),

            Action::make('open_selected_exception')
                ->label('Open environment detail')
                ->icon('heroicon-o-arrow-top-right-on-square')
                ->color('gray')
                ->visible(fn (): bool => $this->selectedFindingException() instanceof FindingException)
                ->url(fn (): ?string => $this->selectedExceptionUrl()),

            Action::make('open_selected_finding')
                ->label('Open finding')
                ->icon('heroicon-o-arrow-top-right-on-square')
                ->color('gray')
                ->visible(fn (): bool => $this->selectedFindingException() instanceof FindingException)
                ->url(fn (): ?string => $this->selectedFindingUrl()),
        ];

        $selectedDecisionActions = [
            Action::make('approve_selected_exception')
                ->label(GovernanceActionCatalog::rule('approve_exception')->canonicalLabel)
                ->color('success')
                ->visible(fn (): bool => $this->selectedFindingException()?->isPending() ?? false)
                ->requiresConfirmation()
                ->modalHeading(GovernanceActionCatalog::rule('approve_exception')->modalHeading)
                ->modalDescription(GovernanceActionCatalog::rule('approve_exception')->modalDescription)
                ->form([
                    DateTimePicker::make('effective_from')
                        ->label('Effective from')
                        ->required()
                        ->seconds(false),
                    DateTimePicker::make('expires_at')
                        ->label('Expires at')
                        ->required()
                        ->seconds(false),
                    Textarea::make('approval_reason')
                        ->label('Approval reason')
                        ->rows(3)
                        ->required()
                        ->maxLength(2000),
                ])
                ->action(function (array $data, FindingExceptionService $service): void {
                    $record = $this->selectedFindingException();
                    $user = auth()->user();

                    if (! $record instanceof FindingException || ! $user instanceof User) {
                        abort(404);
                    }

                    $wasRenewalRequest = $record->isPendingRenewal();
                    $updated = $service->approve($record, $user, $data);
                    $this->selectedFindingExceptionId = (int) $updated->getKey();
                    $this->resetTable();

                    Notification::make()
                        ->title($wasRenewalRequest ? 'Exception renewed' : GovernanceActionCatalog::rule('approve_exception')->successTitle)
                        ->success()
                        ->send();
                }),

            Action::make('reject_selected_exception')
                ->label(GovernanceActionCatalog::rule('reject_exception')->canonicalLabel)
                ->color('warning')
                ->visible(fn (): bool => $this->selectedFindingException()?->isPending() ?? false)
                ->requiresConfirmation()
                ->modalHeading(GovernanceActionCatalog::rule('reject_exception')->modalHeading)
                ->modalDescription(GovernanceActionCatalog::rule('reject_exception')->modalDescription)
                ->form([
                    Textarea::make('rejection_reason')
                        ->label('Rejection reason')
                        ->rows(3)
                        ->required()
                        ->maxLength(2000),
                ])
                ->action(function (array $data, FindingExceptionService $service): void {
                    $record = $this->selectedFindingException();
                    $user = auth()->user();

                    if (! $record instanceof FindingException || ! $user instanceof User) {
                        abort(404);
                    }

                    $wasRenewalRequest = $record->isPendingRenewal();
                    $updated = $service->reject($record, $user, $data);
                    $this->selectedFindingExceptionId = (int) $updated->getKey();
                    $this->resetTable();

                    Notification::make()
                        ->title($wasRenewalRequest ? 'Renewal rejected' : GovernanceActionCatalog::rule('reject_exception')->successTitle)
                        ->success()
                        ->send();
                }),
        ];

        $actions[] = ActionGroup::make($selectedContextActions)
            ->label('Selected context')
            ->icon('heroicon-o-rectangle-stack')
            ->color('gray')
            ->visible(fn (): bool => $this->selectedFindingException() instanceof FindingException);

        $actions[] = ActionGroup::make($selectedDecisionActions)
            ->label('Review selected')
            ->icon('heroicon-o-shield-check')
            ->color('primary')
            ->visible(fn (): bool => $this->selectedFindingException()?->isPending() ?? false);

        return $actions;
    }

    public function table(Table $table): Table
    {
        return $table
            ->query(fn (): Builder => $this->queueBaseQuery())
            ->defaultSort('requested_at', 'asc')
            ->paginated(TablePaginationProfiles::customPage())
            ->persistFiltersInSession()
            ->persistSearchInSession()
            ->persistSortInSession()
            ->columns([
                TextColumn::make('status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::FindingExceptionStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::FindingExceptionStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::FindingExceptionStatus))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::FindingExceptionStatus)),
                TextColumn::make('current_validity_state')
                    ->label('Validity')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::FindingRiskGovernanceValidity))
                    ->color(BadgeRenderer::color(BadgeDomain::FindingRiskGovernanceValidity))
                    ->icon(BadgeRenderer::icon(BadgeDomain::FindingRiskGovernanceValidity))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::FindingRiskGovernanceValidity)),
                TextColumn::make('tenant.name')
                    ->label('Environment')
                    ->searchable(),
                TextColumn::make('finding_summary')
                    ->label('Finding')
                    ->state(fn (FindingException $record): string => $record->finding?->resolvedSubjectDisplayName() ?: 'Finding #'.$record->finding_id)
                    ->searchable(),
                TextColumn::make('governance_warning')
                    ->label('Governance warning')
                    ->state(fn (FindingException $record): ?string => $this->governanceWarning($record))
                    ->color(fn (FindingException $record): string => $this->governanceWarningColor($record))
                    ->wrap(),
                TextColumn::make('requester.name')
                    ->label('Requested by')
                    ->placeholder('—'),
                TextColumn::make('owner.name')
                    ->label('Owner')
                    ->placeholder('—'),
                TextColumn::make('review_due_at')
                    ->label('Review due')
                    ->dateTime()
                    ->placeholder('—')
                    ->sortable(),
                TextColumn::make('expires_at')
                    ->label('Expires')
                    ->dateTime()
                    ->placeholder('—')
                    ->sortable(),
                TextColumn::make('requested_at')
                    ->label('Requested')
                    ->dateTime()
                    ->sortable(),
            ])
            ->filters([
                SelectFilter::make('managed_environment_id')
                    ->label('Environment')
                    ->options(fn (): array => $this->tenantFilterOptions())
                    ->searchable(),
                SelectFilter::make('status')
                    ->options(FilterOptionCatalog::findingExceptionStatuses()),
                SelectFilter::make('current_validity_state')
                    ->label('Validity')
                    ->options(FilterOptionCatalog::findingExceptionValidityStates()),
            ])
            ->actions([
                Action::make('inspect_exception')
                    ->label('Inspect exception')
                    ->icon('heroicon-o-eye')
                    ->color('gray')
                    ->url(fn (FindingException $record): string => $this->queueUrl(['exception' => (int) $record->getKey()])),
            ])
            ->bulkActions([])
            ->emptyStateHeading('No exceptions match this queue')
            ->emptyStateDescription('Adjust the current environment or lifecycle filters to review governed exceptions in this workspace.')
            ->emptyStateIcon('heroicon-o-shield-check')
            ->emptyStateActions([
                Action::make('clear_filters')
                    ->label('Clear filters')
                    ->icon('heroicon-o-x-mark')
                    ->color('gray')
                    ->action(fn (): mixed => $this->clearQueueFilters()),
            ]);
    }

    public function updatedTableFilters(): void
    {
        $this->normalizeSelectedFindingExceptionId();
    }

    public function updatedTableSearch(): void
    {
        $this->normalizeSelectedFindingExceptionId();
    }

    public function selectedFindingException(): ?FindingException
    {
        if (! is_int($this->selectedFindingExceptionId)) {
            return null;
        }

        $this->normalizeSelectedFindingExceptionId();

        if (! is_int($this->selectedFindingExceptionId)) {
            return null;
        }

        try {
            return $this->resolveSelectedFindingException($this->selectedFindingExceptionId);
        } catch (NotFoundHttpException) {
            return null;
        }
    }

    public function selectedExceptionUrl(): ?string
    {
        $record = $this->selectedFindingException();

        if (! $record instanceof FindingException || ! $record->tenant) {
            return null;
        }

        return $this->appendQuery(
            FindingExceptionResource::getUrl('view', ['record' => $record], tenant: $record->tenant),
            $this->navigationContext()?->toQuery() ?? [],
        );
    }

    public function selectedFindingUrl(): ?string
    {
        $record = $this->selectedFindingException();

        if (! $record instanceof FindingException || ! $record->finding || ! $record->tenant) {
            return null;
        }

        return $this->appendQuery(
            FindingResource::getUrl('view', ['record' => $record->finding], tenant: $record->tenant),
            $this->navigationContext()?->toQuery() ?? [],
        );
    }

    public function clearSelectedException(): void
    {
        $this->selectedFindingExceptionId = null;
    }

    public function clearQueueFilters(): void
    {
        $hadEnvironmentFilter = $this->currentTenantFilterId() !== null;

        $this->removeTableFilter('managed_environment_id');
        $this->removeTableFilter('status');
        $this->removeTableFilter('current_validity_state');
        $this->selectedFindingExceptionId = null;
        $this->clearWorkspaceHubEnvironmentFilterState(request());
        $this->resetTable();

        if ($hadEnvironmentFilter) {
            $this->redirectToCleanWorkspaceHubUrl(static::getUrl(panel: 'admin'), request());
        }
    }

    /**
     * @return array{label: string, clear_url: string}|null
     */
    public function environmentFilterChip(): ?array
    {
        $tenant = $this->filteredTenant();

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

        return [
            'label' => (string) $tenant->name,
            'clear_url' => $this->cleanWorkspaceHubUrl(static::getUrl(panel: 'admin')),
        ];
    }

    /**
     * @return array<int, ManagedEnvironment>
     */
    public function authorizedTenants(): array
    {
        if ($this->authorizedTenants !== null) {
            return $this->authorizedTenants;
        }

        $user = auth()->user();

        if (! $user instanceof User) {
            return $this->authorizedTenants = [];
        }

        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());

        if (! is_int($workspaceId)) {
            return $this->authorizedTenants = [];
        }

        $tenants = $user->accessibleManagedEnvironmentsQuery($workspaceId)
            ->orderBy('managed_environments.name')
            ->get();

        return $this->authorizedTenants = $tenants
            ->filter(fn (ManagedEnvironment $tenant): bool => (int) $tenant->workspace_id === $workspaceId)
            ->values()
            ->all();
    }

    private function queueBaseQuery(): Builder
    {
        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());
        $tenantIds = array_values(array_map(
            static fn (ManagedEnvironment $tenant): int => (int) $tenant->getKey(),
            $this->authorizedTenants(),
        ));

        return FindingException::query()
            ->with([
                'tenant',
                'requester',
                'owner',
                'approver',
                'finding' => fn ($query) => $query->withSubjectDisplayName(),
                'decisions.actor',
                'evidenceReferences',
            ])
            ->where('workspace_id', (int) $workspaceId)
            ->whereIn('managed_environment_id', $tenantIds === [] ? [-1] : $tenantIds);
    }

    /**
     * @return array<string, string>
     */
    private function tenantFilterOptions(): array
    {
        return Collection::make($this->authorizedTenants())
            ->mapWithKeys(static fn (ManagedEnvironment $tenant): array => [
                (string) $tenant->getKey() => $tenant->name,
            ])
            ->all();
    }

    private function applyRequestedTenantPrefilter(): void
    {
        $workspace = $this->currentWorkspace();

        if (! $workspace instanceof Workspace) {
            return;
        }

        $filter = WorkspaceHubEnvironmentFilter::fromRequest(request(), $workspace);

        if (! $filter instanceof WorkspaceHubEnvironmentFilter) {
            return;
        }

        $environmentId = $filter->environmentId();

        foreach ($this->authorizedTenants() as $tenant) {
            if ((int) $tenant->getKey() === $environmentId) {
                $this->tableFilters['managed_environment_id']['value'] = (string) $environmentId;
                $this->tableDeferredFilters['managed_environment_id']['value'] = (string) $environmentId;

                return;
            }
        }

        throw new NotFoundHttpException;
    }

    private function filteredTenant(): ?ManagedEnvironment
    {
        $tenantId = $this->currentTenantFilterId();

        if (! is_int($tenantId)) {
            return null;
        }

        foreach ($this->authorizedTenants() as $tenant) {
            if ((int) $tenant->getKey() === $tenantId) {
                return $tenant;
            }
        }

        return null;
    }

    private function currentTenantFilterId(): ?int
    {
        $tenantFilter = app(CanonicalAdminEnvironmentFilterState::class)->currentFilterValue(
            $this->getTableFiltersSessionKey(),
            $this->tableFilters ?? [],
            request(),
        );

        return is_numeric($tenantFilter) ? (int) $tenantFilter : null;
    }

    private function hasActiveQueueFilters(): bool
    {
        return $this->currentTenantFilterId() !== null
            || is_string(data_get($this->tableFilters, 'status.value'))
            || is_string(data_get($this->tableFilters, 'current_validity_state.value'));
    }

    private function resolveSelectedFindingException(int $findingExceptionId): FindingException
    {
        $record = $this->queueBaseQuery()
            ->whereKey($findingExceptionId)
            ->first();

        if (! $record instanceof FindingException) {
            throw new NotFoundHttpException;
        }

        return $record;
    }

    private function queueUrl(array $overrides = []): string
    {
        $parameters = array_merge(
            $this->navigationContext()?->toQuery() ?? [],
            [
                'environment_id' => $this->filteredTenant()?->getKey(),
                'exception' => $this->selectedFindingExceptionId,
            ],
            $overrides,
        );

        return static::getUrl(
            panel: 'admin',
            parameters: array_filter($parameters, static fn (mixed $value): bool => $value !== null && $value !== '' && $value !== []),
        );
    }

    private function currentWorkspace(): ?Workspace
    {
        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());

        if (! is_int($workspaceId)) {
            return null;
        }

        return Workspace::query()->whereKey($workspaceId)->first();
    }

    private function navigationContext(): ?CanonicalNavigationContext
    {
        return CanonicalNavigationContext::fromRequest(request());
    }

    private function incomingGovernanceContext(): ?CanonicalNavigationContext
    {
        $context = $this->navigationContext();

        return $context?->sourceSurface === 'governance.inbox'
            ? $context
            : null;
    }

    private function normalizeSelectedFindingExceptionId(): void
    {
        if (! is_int($this->selectedFindingExceptionId) || $this->selectedFindingExceptionId <= 0) {
            $this->selectedFindingExceptionId = null;

            return;
        }

        $this->selectedFindingExceptionId = $this->resolveSelectedFindingExceptionId($this->selectedFindingExceptionId);
    }

    private function resolveSelectedFindingExceptionId(int $findingExceptionId): ?int
    {
        try {
            $record = $this->resolveSelectedFindingException($findingExceptionId);
        } catch (NotFoundHttpException) {
            return null;
        }

        return $this->selectedFindingExceptionVisible((int) $record->getKey())
            ? (int) $record->getKey()
            : null;
    }

    private function selectedFindingExceptionVisible(int $findingExceptionId): bool
    {
        $record = $this->resolveSelectedFindingException($findingExceptionId);

        return $this->matchesSelectedFindingExceptionFilters($record)
            && $this->matchesSelectedFindingExceptionSearch($record);
    }

    /**
     * @return array<string, mixed>
     */
    private function currentQueueFiltersState(): array
    {
        $persisted = session()->get($this->getTableFiltersSessionKey(), []);

        return array_replace_recursive(
            is_array($persisted) ? $persisted : [],
            $this->tableFilters ?? [],
        );
    }

    private function currentQueueSearchState(): string
    {
        $search = trim((string) ($this->tableSearch ?? ''));

        if ($search !== '') {
            return $search;
        }

        $persisted = session()->get($this->getTableSearchSessionKey(), '');

        return trim(is_string($persisted) ? $persisted : '');
    }

    private function matchesSelectedFindingExceptionFilters(FindingException $record): bool
    {
        $filters = $this->currentQueueFiltersState();

        $tenantFilter = data_get($filters, 'managed_environment_id.value');

        if (is_numeric($tenantFilter) && (int) $record->managed_environment_id !== (int) $tenantFilter) {
            return false;
        }

        $statusFilter = data_get($filters, 'status.value');

        if (is_string($statusFilter) && $statusFilter !== '' && (string) $record->status !== $statusFilter) {
            return false;
        }

        $validityFilter = data_get($filters, 'current_validity_state.value');

        if (is_string($validityFilter) && $validityFilter !== '' && (string) $record->current_validity_state !== $validityFilter) {
            return false;
        }

        return true;
    }

    private function matchesSelectedFindingExceptionSearch(FindingException $record): bool
    {
        $search = Str::lower($this->currentQueueSearchState());

        if ($search === '') {
            return true;
        }

        $haystack = Str::lower(implode(' ', [
            $record->tenant?->name ?? '',
            $record->finding?->resolvedSubjectDisplayName() ?? 'Finding #'.$record->finding_id,
            $record->request_reason ?? '',
        ]));

        return str_contains($haystack, $search);
    }

    private function governanceWarning(FindingException $record): ?string
    {
        $finding = $record->relationLoaded('finding')
            ? $record->finding
            : $record->finding()->withSubjectDisplayName()->first();

        if (! $finding instanceof \App\Models\Finding) {
            return null;
        }

        return app(FindingRiskGovernanceResolver::class)->resolveWarningMessage($finding, $record);
    }

    private function governanceWarningColor(FindingException $record): string
    {
        if ((string) $record->current_validity_state === FindingException::VALIDITY_EXPIRING) {
            return 'warning';
        }

        $finding = $record->relationLoaded('finding')
            ? $record->finding
            : $record->finding()->withSubjectDisplayName()->first();

        if ($finding instanceof \App\Models\Finding && $record->requiresFreshDecisionForFinding($finding)) {
            return 'warning';
        }

        return 'danger';
    }

    /**
     * @param  array<string, mixed>  $query
     */
    private function appendQuery(string $url, array $query): string
    {
        if ($query === []) {
            return $url;
        }

        return $url.(str_contains($url, '?') ? '&' : '?').http_build_query($query);
    }
}
