<?php

declare(strict_types=1);

namespace App\Filament\Pages;

use App\Models\InventoryItem;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\Auth\CapabilityResolver;
use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Services\PortfolioCompare\CrossEnvironmentPromotionExecutionService;
use App\Support\Auth\Capabilities;
use App\Support\ManagedEnvironmentLinks;
use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\OperationalControls\OperationalControlBlockedException;
use App\Support\OperationRunLinks;
use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\OpsUx\ProviderOperationStartResultPresenter;
use App\Support\PortfolioCompare\CrossEnvironmentComparePreviewBuilder;
use App\Support\PortfolioCompare\CrossEnvironmentCompareSelection;
use App\Support\PortfolioCompare\CrossEnvironmentPromotionPreflight;
use App\Support\Rbac\WorkspaceUiEnforcement;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
use App\Support\Workspaces\WorkspaceContext;
use BackedEnum;
use DomainException;
use Filament\Actions\Action;
use Filament\Notifications\Notification;
use Filament\Forms\Components\Select;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Pages\Page;
use Filament\Schemas\Components\Grid;
use Filament\Schemas\Schema;
use InvalidArgumentException;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use UnitEnum;

class CrossEnvironmentComparePage extends Page implements HasForms
{
    use InteractsWithForms;

    private const string SOURCE_ENVIRONMENT_QUERY_KEY = 'source_environment_id';

    private const string TARGET_ENVIRONMENT_QUERY_KEY = 'target_environment_id';

    private const string POLICY_TYPE_QUERY_KEY = 'policy_type';

    protected static bool $isDiscovered = false;

    protected static bool $shouldRegisterNavigation = false;

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

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

    protected static ?string $title = 'Cross-environment compare';

    protected static ?string $slug = 'cross-environment-compare';

    protected string $view = 'filament.pages.cross-environment-compare';

    public ?string $sourceEnvironmentId = null;

    public ?string $targetEnvironmentId = null;

    /**
     * @var list<string>
     */
    public array $selectedPolicyTypes = [];

    /**
     * @var array<string, mixed>|null
     */
    public ?array $navigationContextPayload = null;

    /**
     * @var array<string, mixed>|null
     */
    public ?array $preview = null;

    /**
     * @var array<string, mixed>|null
     */
    public ?array $preflight = null;

    public ?string $selectionMessage = null;

    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::ListOnlyReadOnly)
            ->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions preserve return navigation, environment drill-downs, and one dominant promotion-preflight action.')
            ->exempt(ActionSurfaceSlot::InspectAffordance, 'The compare page uses explicit selection controls instead of row-click inspection.')
            ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'Cross-environment compare renders focused subject summaries instead of row-level overflow actions.')
            ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'The compare page has no bulk actions.')
            ->satisfy(ActionSurfaceSlot::ListEmptyState, 'The compare page explains when a selection is incomplete or invalid before any preview exists.')
            ->exempt(ActionSurfaceSlot::DetailHeader, 'Cross-environment compare is a workspace decision page, not a record detail header.');
    }

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

        $this->navigationContextPayload = is_array(request()->query('nav')) ? request()->query('nav') : null;

        $this->hydrateSelectionFromRequest();
        $this->refreshPreview();

        $this->form->fill($this->formState());
    }

    public function form(Schema $schema): Schema
    {
        return $schema
            ->schema([
                Grid::make([
                    'default' => 1,
                    'xl' => 3,
                ])
                    ->schema([
                        Select::make('sourceEnvironmentId')
                            ->label('Source environment')
                            ->options(fn (): array => $this->environmentOptions())
                            ->searchable()
                            ->preload()
                            ->native(false)
                            ->placeholder('Select a source environment')
                            ->extraFieldWrapperAttributes(['data-testid' => 'cross-environment-source'])
                            ->extraInputAttributes(['data-testid' => 'cross-environment-source']),
                        Select::make('targetEnvironmentId')
                            ->label('Target environment')
                            ->options(fn (): array => $this->environmentOptions())
                            ->searchable()
                            ->preload()
                            ->native(false)
                            ->placeholder('Select a target environment')
                            ->extraFieldWrapperAttributes(['data-testid' => 'cross-environment-target'])
                            ->extraInputAttributes(['data-testid' => 'cross-environment-target']),
                        Select::make('selectedPolicyTypes')
                            ->label('Governed subjects')
                            ->options(fn (): array => $this->policyTypeOptions())
                            ->multiple()
                            ->searchable()
                            ->preload()
                            ->native(false)
                            ->placeholder('All governed subjects')
                            ->helperText(fn (): ?string => $this->policyTypeOptions() === []
                                ? 'Governed subject filters appear after authorized environment inventory exists in the active workspace.'
                                : null)
                            ->extraFieldWrapperAttributes(['data-testid' => 'cross-environment-policy-types'])
                            ->extraInputAttributes(['data-testid' => 'cross-environment-policy-types']),
                    ]),
            ]);
    }

    /**
     * @return array<Action>
     */
    protected function getHeaderActions(): array
    {
        $actions = [];

        $navigationContext = $this->navigationContext();

        if ($navigationContext?->backLinkLabel !== null && $navigationContext->backLinkUrl !== null) {
            $actions[] = Action::make('return_to_origin')
                ->label($navigationContext->backLinkLabel)
                ->icon('heroicon-o-arrow-left')
                ->color('gray')
                ->url($navigationContext->backLinkUrl);
        }

        $sourceEnvironment = $this->selectedSourceEnvironment();

        if ($sourceEnvironment instanceof ManagedEnvironment) {
            $actions[] = Action::make('open_source_environment')
                ->label('Open source environment')
                ->icon('heroicon-o-arrow-top-right-on-square')
                ->color('gray')
                ->url(ManagedEnvironmentLinks::viewUrl($sourceEnvironment));
        }

        $targetEnvironment = $this->selectedTargetEnvironment();

        if ($targetEnvironment instanceof ManagedEnvironment) {
            $actions[] = Action::make('open_target_environment')
                ->label('Open target environment')
                ->icon('heroicon-o-arrow-top-right-on-square')
                ->color('gray')
                ->url(ManagedEnvironmentLinks::viewUrl($targetEnvironment));
        }

        $preflightAction = Action::make('generatePromotionPreflight')
            ->label('Generate promotion preflight')
            ->icon('heroicon-o-sparkles')
            ->color('primary')
            ->visible(fn (): bool => ! is_array($this->preflight))
            ->disabled(fn (): bool => $this->preflightDisabledReason() !== null)
            ->tooltip(fn (): ?string => $this->preflightDisabledReason())
            ->action(fn (): mixed => $this->generatePromotionPreflight());

        $preflightAction = WorkspaceUiEnforcement::forAction(
            $preflightAction,
            fn (): ?Workspace => $this->workspace(),
        )
            ->requireCapability(Capabilities::WORKSPACE_BASELINES_MANAGE)
            ->preserveVisibility()
            ->preserveDisabled()
            ->tooltip('You need workspace baseline manage access to generate a promotion preflight.')
            ->apply()
            ->tooltip(function (): ?string {
                $user = auth()->user();
                $workspace = $this->workspace();

                if ($user instanceof User && $workspace instanceof Workspace) {
                    /** @var WorkspaceCapabilityResolver $resolver */
                    $resolver = app(WorkspaceCapabilityResolver::class);

                    if ($resolver->isMember($user, $workspace)
                        && ! $resolver->can($user, $workspace, Capabilities::WORKSPACE_BASELINES_MANAGE)) {
                        return 'You need workspace baseline manage access to generate a promotion preflight.';
                    }
                }

                return $this->preflightDisabledReason();
            });

        $actions[] = $preflightAction;

        $actions[] = Action::make('executePromotion')
            ->label('Execute promotion')
            ->icon('heroicon-o-play')
            ->color('warning')
            ->visible(fn (): bool => is_array($this->preflight))
            ->requiresConfirmation()
            ->modalHeading('Execute promotion')
            ->modalDescription(fn (): string => $this->executePromotionConfirmationDescription())
            ->modalSubmitActionLabel('Queue promotion')
            ->disabled(fn (): bool => $this->executePromotionDisabledReason() !== null)
            ->tooltip(fn (): ?string => $this->executePromotionDisabledReason())
            ->action(fn (): mixed => $this->executePromotion());

        return $actions;
    }

    public function applySelection(): void
    {
        $this->selectionMessage = null;
        $this->preflight = null;

        $this->sourceEnvironmentId = $this->normalizeEnvironmentIdentifier($this->sourceEnvironmentId);
        $this->targetEnvironmentId = $this->normalizeEnvironmentIdentifier($this->targetEnvironmentId);
        $this->selectedPolicyTypes = $this->normalizePolicyTypes($this->selectedPolicyTypes);

        if ($this->sourceEnvironmentId !== null
            && $this->targetEnvironmentId !== null
            && $this->sourceEnvironmentId === $this->targetEnvironmentId) {
            $this->selectionMessage = 'Choose two different environments.';
            $this->addError('targetEnvironmentId', $this->selectionMessage);

            return;
        }

        $this->redirect($this->selectionUrl(), navigate: true);
    }

    public function generatePromotionPreflight(): void
    {
        $this->authorizePageAccess();
        $this->authorizePreflightExecution();

        if ($this->preview === null) {
            $this->refreshPreview();
        }

        if ($this->preview === null) {
            return;
        }

        $selection = $this->compareSelection();

        if (! $selection instanceof CrossEnvironmentCompareSelection) {
            return;
        }

        $this->preflight = app(CrossEnvironmentPromotionPreflight::class)->build($this->preview);

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

        if ($workspace instanceof Workspace && $user instanceof User) {
            app(WorkspaceAuditLogger::class)->logCrossEnvironmentPromotionPreflightGenerated(
                workspace: $workspace,
                sourceEnvironment: $selection->sourceEnvironment,
                targetEnvironment: $selection->targetEnvironment,
                preflight: $this->preflight,
                actor: $user,
            );
        }
    }

    public function executePromotion(): void
    {
        $this->authorizePageAccess();
        $this->authorizePromotionExecution();

        if (! is_array($this->preview) || ! is_array($this->preflight)) {
            Notification::make()
                ->title('Promotion execution unavailable')
                ->body('Generate a current promotion preflight before executing promotion.')
                ->warning()
                ->send();

            return;
        }

        $selection = $this->compareSelection();
        $user = auth()->user();

        if (! $selection instanceof CrossEnvironmentCompareSelection || ! $user instanceof User) {
            Notification::make()
                ->title('Promotion execution unavailable')
                ->body('Refresh the compare selection before executing promotion.')
                ->warning()
                ->send();

            return;
        }

        try {
            $result = app(CrossEnvironmentPromotionExecutionService::class)->start(
                selection: $selection,
                preview: $this->preview,
                preflight: $this->preflight,
                actor: $user,
            );
        } catch (OperationalControlBlockedException $exception) {
            Notification::make()
                ->title($exception->title())
                ->body($exception->getMessage())
                ->warning()
                ->send();

            return;
        } catch (DomainException|InvalidArgumentException $exception) {
            Notification::make()
                ->title('Promotion execution unavailable')
                ->body($exception->getMessage())
                ->warning()
                ->send();

            return;
        }

        $notification = app(ProviderOperationStartResultPresenter::class)->notification(
            result: $result,
            blockedTitle: 'Promotion execution blocked',
            runUrl: OperationRunLinks::tenantlessView($result->run),
            scopeBusyTitle: 'Promotion scope busy',
            scopeBusyBody: 'Another promotion or restore operation is already active for this target scope. Open the active operation for progress and next steps.',
        );

        if (in_array($result->status, ['started', 'deduped', 'scope_busy'], true)) {
            OpsUxBrowserEvents::dispatchRunEnqueued($this);
        }

        $notification->send();
    }

    public function clearSelectionUrl(): string
    {
        return static::getUrl($this->routeParameters([
            self::SOURCE_ENVIRONMENT_QUERY_KEY => null,
            self::TARGET_ENVIRONMENT_QUERY_KEY => null,
            self::POLICY_TYPE_QUERY_KEY => null,
        ]), panel: 'admin');
    }

    public function selectionUrl(): string
    {
        return static::getUrl($this->routeParameters(), panel: 'admin');
    }

    public static function launchUrl(
        ?ManagedEnvironment $sourceEnvironment = null,
        ?ManagedEnvironment $targetEnvironment = null,
        ?CanonicalNavigationContext $navigationContext = null,
    ): string {
        $parameters = [];

        if ($sourceEnvironment instanceof ManagedEnvironment) {
            $parameters[self::SOURCE_ENVIRONMENT_QUERY_KEY] = (int) $sourceEnvironment->getKey();
        }

        if ($targetEnvironment instanceof ManagedEnvironment) {
            $parameters[self::TARGET_ENVIRONMENT_QUERY_KEY] = (int) $targetEnvironment->getKey();
        }

        if ($navigationContext instanceof CanonicalNavigationContext) {
            $parameters = array_replace($parameters, $navigationContext->toQuery());
        }

        return static::getUrl($parameters, panel: 'admin');
    }

    public function hasActiveSelection(): bool
    {
        return $this->sourceEnvironmentId !== null
            || $this->targetEnvironmentId !== null
            || $this->selectedPolicyTypes !== [];
    }

    public function stateColor(string $state): string
    {
        return match ($state) {
            'match', 'ready' => 'success',
            'different', 'manual_mapping_required' => 'warning',
            'missing' => 'info',
            'ambiguous' => 'gray',
            'blocked' => 'danger',
            default => 'gray',
        };
    }

    public function stateLabel(string $value): string
    {
        return Str::headline(str_replace('_', ' ', $value));
    }

    public function reasonLabel(string $reasonCode): string
    {
        return Str::headline(str_replace('_', ' ', $reasonCode));
    }

    public function sourceEnvironmentUrl(): ?string
    {
        $environment = $this->selectedSourceEnvironment();

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

        return ManagedEnvironmentLinks::viewUrl($environment);
    }

    public function targetEnvironmentUrl(): ?string
    {
        $environment = $this->selectedTargetEnvironment();

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

        return ManagedEnvironmentLinks::viewUrl($environment);
    }

    /**
     * @return array<string, mixed>
     */
    private function formState(): array
    {
        return [
            'sourceEnvironmentId' => $this->sourceEnvironmentId,
            'targetEnvironmentId' => $this->targetEnvironmentId,
            'selectedPolicyTypes' => $this->selectedPolicyTypes,
        ];
    }

    private function hydrateSelectionFromRequest(): void
    {
        $this->sourceEnvironmentId = $this->normalizeEnvironmentIdentifier(request()->query(self::SOURCE_ENVIRONMENT_QUERY_KEY));
        $this->targetEnvironmentId = $this->normalizeEnvironmentIdentifier(request()->query(self::TARGET_ENVIRONMENT_QUERY_KEY));
        $this->selectedPolicyTypes = $this->normalizePolicyTypes(request()->query(self::POLICY_TYPE_QUERY_KEY, []));
    }

    private function refreshPreview(): void
    {
        $this->selectionMessage = null;
        $this->preview = null;
        $this->preflight = null;

        $selection = $this->compareSelection();

        if (! $selection instanceof CrossEnvironmentCompareSelection) {
            return;
        }

        $this->preview = app(CrossEnvironmentComparePreviewBuilder::class)->build($selection);
    }

    private function authorizePageAccess(): void
    {
        $user = auth()->user();
        $workspace = $this->workspace();

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

        if (! $workspace instanceof Workspace) {
            abort(404);
        }

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

        if (! $resolver->isMember($user, $workspace)) {
            abort(404);
        }

        if (! $resolver->can($user, $workspace, Capabilities::WORKSPACE_BASELINES_VIEW)) {
            abort(403);
        }
    }

    private function authorizePreflightExecution(): void
    {
        $user = auth()->user();
        $workspace = $this->workspace();

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

        if (! $workspace instanceof Workspace) {
            abort(404);
        }

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

        if (! $resolver->isMember($user, $workspace)) {
            abort(404);
        }

        if (! $resolver->can($user, $workspace, Capabilities::WORKSPACE_BASELINES_MANAGE)) {
            abort(403);
        }
    }

    private function authorizePromotionExecution(): void
    {
        $this->authorizePreflightExecution();

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

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

        $targetEnvironment = $this->selectedTargetEnvironment();

        if (! $targetEnvironment instanceof ManagedEnvironment) {
            abort(404);
        }

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

        if (! $resolver->can($user, $targetEnvironment, Capabilities::TENANT_MANAGE)) {
            abort(403);
        }
    }

    private function compareSelection(): ?CrossEnvironmentCompareSelection
    {
        $sourceEnvironment = $this->selectedSourceEnvironment();
        $targetEnvironment = $this->selectedTargetEnvironment();

        if (! $sourceEnvironment instanceof ManagedEnvironment || ! $targetEnvironment instanceof ManagedEnvironment) {
            return null;
        }

        if ((int) $sourceEnvironment->getKey() === (int) $targetEnvironment->getKey()) {
            $this->selectionMessage = 'Choose two different environments.';

            return null;
        }

        return new CrossEnvironmentCompareSelection(
            sourceEnvironment: $sourceEnvironment,
            targetEnvironment: $targetEnvironment,
            policyTypes: $this->selectedPolicyTypes,
        );
    }

    private function selectedSourceEnvironment(): ?ManagedEnvironment
    {
        if ($this->sourceEnvironmentId === null) {
            return null;
        }

        return $this->resolveAuthorizedEnvironment($this->sourceEnvironmentId);
    }

    private function selectedTargetEnvironment(): ?ManagedEnvironment
    {
        if ($this->targetEnvironmentId === null) {
            return null;
        }

        return $this->resolveAuthorizedEnvironment($this->targetEnvironmentId);
    }

    private function resolveAuthorizedEnvironment(string $environmentId): ManagedEnvironment
    {
        $workspace = $this->workspace();
        $user = auth()->user();

        if (! $workspace instanceof Workspace || ! $user instanceof User) {
            abort(404);
        }

        $environment = ManagedEnvironment::query()
            ->where('workspace_id', (int) $workspace->getKey())
            ->whereKey((int) $environmentId)
            ->first();

        if (! $environment instanceof ManagedEnvironment) {
            abort(404);
        }

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

        if (! $user->canAccessTenant($environment) || ! $resolver->can($user, $environment, Capabilities::TENANT_VIEW)) {
            abort(404);
        }

        return $environment;
    }

    /**
     * @return array<string, string>
     */
    private function environmentOptions(): array
    {
        $workspace = $this->workspace();
        $user = auth()->user();

        if (! $workspace instanceof Workspace || ! $user instanceof User) {
            return [];
        }

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

        $environments = $user->accessibleManagedEnvironmentsQuery((int) $workspace->getKey())
            ->select('managed_environments.*')
            ->orderBy('managed_environments.name')
            ->get();

        $resolver->primeMemberships($user, $environments->modelKeys());

        return $environments
            ->filter(fn (ManagedEnvironment $environment): bool => $resolver->can($user, $environment, Capabilities::TENANT_VIEW))
            ->mapWithKeys(fn (ManagedEnvironment $environment): array => [
                (string) $environment->getKey() => (string) $environment->name,
            ])
            ->all();
    }

    /**
     * @return array<string, string>
     */
    private function policyTypeOptions(): array
    {
        $environmentIds = array_map(static fn (string $environmentId): int => (int) $environmentId, array_keys($this->environmentOptions()));

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

        return InventoryItem::query()
            ->whereIn('managed_environment_id', $environmentIds)
            ->whereNotNull('policy_type')
            ->where('policy_type', '!=', '')
            ->distinct()
            ->orderBy('policy_type')
            ->pluck('policy_type')
            ->mapWithKeys(fn (string $policyType): array => [
                $policyType => Str::headline($policyType),
            ])
            ->all();
    }

    private function preflightDisabledReason(): ?string
    {
        if ($this->selectionMessage !== null) {
            return $this->selectionMessage;
        }

        if (! is_array($this->preview)) {
            return 'Select an authorized source and target environment to generate a promotion preflight.';
        }

        if ((int) data_get($this->preview, 'summary.total', 0) === 0) {
            return 'No governed subjects are available for this compare selection yet.';
        }

        return null;
    }

    private function executePromotionDisabledReason(): ?string
    {
        if ($this->selectionMessage !== null) {
            return $this->selectionMessage;
        }

        if (! is_array($this->preview)) {
            return 'Run compare preview before executing promotion.';
        }

        if (! is_array($this->preflight)) {
            return 'Generate a current promotion preflight before executing promotion.';
        }

        if ((int) data_get($this->preflight, 'summary.ready', 0) <= 0) {
            return 'Current promotion preflight has no ready governed subjects to execute.';
        }

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

        if ($user instanceof User && $workspace instanceof Workspace) {
            /** @var WorkspaceCapabilityResolver $workspaceResolver */
            $workspaceResolver = app(WorkspaceCapabilityResolver::class);

            if ($workspaceResolver->isMember($user, $workspace)
                && ! $workspaceResolver->can($user, $workspace, Capabilities::WORKSPACE_BASELINES_MANAGE)) {
                return 'You need workspace baseline manage access to execute promotion.';
            }

            $targetEnvironment = $this->selectedTargetEnvironment();

            if ($targetEnvironment instanceof ManagedEnvironment) {
                /** @var CapabilityResolver $resolver */
                $resolver = app(CapabilityResolver::class);

                if (! $resolver->can($user, $targetEnvironment, Capabilities::TENANT_MANAGE)) {
                    return 'You need target environment manage access to execute promotion.';
                }
            }
        }

        return null;
    }

    private function executePromotionConfirmationDescription(): string
    {
        $selection = $this->compareSelection();
        $ready = (int) data_get($this->preflight, 'summary.ready', 0);
        $blocked = (int) data_get($this->preflight, 'summary.blocked', 0);
        $manualMappingRequired = (int) data_get($this->preflight, 'summary.manual_mapping_required', 0);
        $excluded = $blocked + $manualMappingRequired;

        $sourceEnvironmentName = $selection?->sourceEnvironment->name ?? 'Source environment';
        $targetEnvironmentName = $selection?->targetEnvironment->name ?? 'Target environment';

        return sprintf(
            'Queue one promotion run from %s to %s for %d ready governed subject%s. %d subject%s remain excluded on the compare page.',
            $sourceEnvironmentName,
            $targetEnvironmentName,
            $ready,
            $ready === 1 ? '' : 's',
            $excluded,
            $excluded === 1 ? '' : 's',
        );
    }

    /**
     * @param  mixed  $value
     */
    private function normalizeEnvironmentIdentifier(mixed $value): ?string
    {
        if (! is_string($value) && ! is_int($value)) {
            return null;
        }

        $normalized = trim((string) $value);

        return is_numeric($normalized) && (int) $normalized > 0 ? (string) (int) $normalized : null;
    }

    /**
     * @param  mixed  $value
     * @return list<string>
     */
    private function normalizePolicyTypes(mixed $value): array
    {
        $allowed = array_fill_keys(array_keys($this->policyTypeOptions()), true);

        $values = match (true) {
            is_string($value) && $value !== '' => [$value],
            is_array($value) => $value,
            default => [],
        };

        return array_values(array_filter(array_unique(array_map(
            static fn (mixed $item): string => is_string($item) ? trim($item) : '',
            $values,
        )), static fn (string $item): bool => $item !== '' && isset($allowed[$item])));
    }

    /**
     * @param  array<string, mixed>  $overrides
     * @return array<string, mixed>
     */
    private function routeParameters(array $overrides = []): array
    {
        $parameters = [
            self::SOURCE_ENVIRONMENT_QUERY_KEY => $this->sourceEnvironmentId,
            self::TARGET_ENVIRONMENT_QUERY_KEY => $this->targetEnvironmentId,
            self::POLICY_TYPE_QUERY_KEY => $this->selectedPolicyTypes,
        ];

        if (is_array($this->navigationContextPayload)) {
            $parameters['nav'] = $this->navigationContextPayload;
        }

        foreach ($overrides as $key => $value) {
            $parameters[$key] = $value;
        }

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

    private function navigationContext(): ?CanonicalNavigationContext
    {
        if (! is_array($this->navigationContextPayload)) {
            return CanonicalNavigationContext::fromRequest(request());
        }

        return CanonicalNavigationContext::fromPayload($this->navigationContextPayload);
    }

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

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

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

        return $workspace instanceof Workspace ? $workspace : null;
    }
}
