<?php

declare(strict_types=1);

namespace App\Support\Workspaces;

use App\Filament\Pages\ChooseEnvironment;
use App\Filament\Pages\Findings\FindingsHygieneReport;
use App\Filament\Pages\Findings\MyFindingsInbox;
use App\Filament\Resources\FindingResource;
use App\Models\AlertDelivery;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentTriageReview;
use App\Models\OperationRun;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Auth\CapabilityResolver;
use App\Services\Auth\ManagedEnvironmentAccessScopeResolver;
use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Services\Auth\WorkspaceRoleCapabilityMap;
use App\Services\Findings\FindingAssignmentHygieneService;
use App\Support\Auth\Capabilities;
use App\Support\BackupHealth\TenantBackupHealthAssessment;
use App\Support\BackupHealth\TenantBackupHealthResolver;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Baselines\BaselineCompareSummaryAssessment;
use App\Support\Baselines\TenantGovernanceAggregate;
use App\Support\Baselines\TenantGovernanceAggregateResolver;
use App\Support\ManagedEnvironmentLinks;
use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\OperationCatalog;
use App\Support\OperationRunLinks;
use App\Support\OpsUx\OperationUxPresenter;
use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewStateResolver;
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
use App\Support\Rbac\UiTooltips;
use App\Support\RestoreSafety\RestoreResultAttention;
use App\Support\RestoreSafety\RestoreSafetyCopy;
use App\Support\RestoreSafety\RestoreSafetyResolver;
use App\Support\Tenants\TenantRecoveryTriagePresentation;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

final class WorkspaceOverviewBuilder
{
    public function __construct(
        private WorkspaceCapabilityResolver $workspaceCapabilityResolver,
        private CapabilityResolver $capabilityResolver,
        private ManagedEnvironmentAccessScopeResolver $managedEnvironmentAccessScopeResolver,
        private FindingAssignmentHygieneService $findingAssignmentHygieneService,
        private TenantGovernanceAggregateResolver $tenantGovernanceAggregateResolver,
        private TenantBackupHealthResolver $tenantBackupHealthResolver,
        private RestoreSafetyResolver $restoreSafetyResolver,
        private ManagedEnvironmentTriageReviewStateResolver $managedEnvironmentTriageReviewStateResolver,
    ) {}

    /**
     * @return array<string, mixed>
     */
    public function build(Workspace $workspace, User $user): array
    {
        $workspaceId = (int) $workspace->getKey();
        $accessibleTenants = $this->accessibleTenants($workspace, $user);
        $accessibleTenantIds = $accessibleTenants
            ->pluck('id')
            ->map(static fn (mixed $id): int => (int) $id)
            ->all();

        $this->capabilityResolver->primeMemberships($user, $accessibleTenantIds);
        $visibleFindingsTenantIds = $this->visibleFindingTenantIds($accessibleTenants, $user);

        $canViewAlerts = $this->workspaceCapabilityResolver->can($user, $workspace, Capabilities::ALERTS_VIEW);
        $navigationContext = $this->workspaceOverviewNavigationContext();
        $tenantContexts = $this->tenantContexts($accessibleTenants, $workspaceId, $canViewAlerts);
        $triageReviewProgress = $this->triageReviewProgress($workspaceId, $tenantContexts);
        $attentionItems = $this->attentionItems($tenantContexts, $user, $canViewAlerts, $navigationContext);

        $governanceAttentionTenantCount = count(array_filter(
            $tenantContexts,
            static fn (array $context): bool => (bool) ($context['has_governance_attention'] ?? false),
        ));
        $backupAttentionTenantCount = count(array_filter(
            $tenantContexts,
            static fn (array $context): bool => (bool) ($context['has_backup_attention'] ?? false),
        ));
        $recoveryAttentionTenantCount = count(array_filter(
            $tenantContexts,
            static fn (array $context): bool => (bool) ($context['has_recovery_attention'] ?? false),
        ));

        $totalProblemOperationsCount = array_sum(array_map(
            static fn (array $context): int => (int) ($context['terminal_follow_up_operations_count'] ?? 0)
                + (int) ($context['stale_attention_operations_count'] ?? 0),
            $tenantContexts,
        ));

        $totalActiveOperationsCount = (int) $this->scopeToAuthorizedTenants(
            OperationRun::query(),
            $workspaceId,
            $accessibleTenantIds,
        )
            ->healthyActive()
            ->count();

        $totalAlertFailuresCount = $canViewAlerts
            ? (int) $this->scopeToAuthorizedTenants(
                AlertDelivery::query(),
                $workspaceId,
                $accessibleTenantIds,
            )
                ->where('created_at', '>=', now()->subDays(7))
                ->where('status', AlertDelivery::STATUS_FAILED)
                ->count()
            : 0;

        $calmness = $this->calmnessState(
            accessibleTenantCount: $accessibleTenants->count(),
            attentionItems: $attentionItems,
            governanceAttentionTenantCount: $governanceAttentionTenantCount,
            backupAttentionTenantCount: $backupAttentionTenantCount,
            recoveryAttentionTenantCount: $recoveryAttentionTenantCount,
            totalProblemOperationsCount: $totalProblemOperationsCount,
            totalActiveOperationsCount: $totalActiveOperationsCount,
            totalAlertFailuresCount: $totalAlertFailuresCount,
            canViewAlerts: $canViewAlerts,
            navigationContext: $navigationContext,
        );

        $attentionEmptyState = [
            'title' => $calmness['title'],
            'body' => $calmness['body'],
            'action_label' => $calmness['next_action']['label'] ?? __('localization.shell.choose_environment'),
            'action_url' => $calmness['next_action']['url'] ?? ChooseEnvironment::getUrl(panel: 'admin'),
        ];

        $myFindingsSignal = $this->myFindingsSignal($workspaceId, $visibleFindingsTenantIds, $user);
        $findingsHygieneSignal = $this->findingsHygieneSignal($workspace, $visibleFindingsTenantIds);

        $zeroTenantState = null;

        if ($accessibleTenants->isEmpty()) {
            $zeroTenantState = $attentionEmptyState;
        }

        $summaryMetrics = $this->summaryMetrics(
            accessibleTenantCount: $accessibleTenants->count(),
            governanceAttentionTenantCount: $governanceAttentionTenantCount,
            backupAttentionTenantCount: $backupAttentionTenantCount,
            recoveryAttentionTenantCount: $recoveryAttentionTenantCount,
            totalActiveOperationsCount: $totalActiveOperationsCount,
            totalAlertFailuresCount: $totalAlertFailuresCount,
            canViewAlerts: $canViewAlerts,
            tenantContexts: $tenantContexts,
            triageReviewSummaries: $triageReviewProgress['summaries'],
            user: $user,
            navigationContext: $navigationContext,
        );

        $recentOperations = $this->recentOperations($workspaceId, $accessibleTenantIds, $navigationContext);
        $quickActions = $this->quickActions(
            workspace: $workspace,
            accessibleTenantCount: $accessibleTenants->count(),
            canViewAlerts: $canViewAlerts,
            user: $user,
            navigationContext: $navigationContext,
        );

        return [
            'workspace' => [
                'id' => $workspaceId,
                'name' => (string) $workspace->name,
                'slug' => filled($workspace->slug) ? (string) $workspace->slug : null,
            ],
            'workspace_id' => $workspaceId,
            'workspace_name' => (string) $workspace->name,
            'accessible_tenant_count' => $accessibleTenants->count(),
            'my_findings_signal' => $myFindingsSignal,
            'findings_hygiene_signal' => $findingsHygieneSignal,
            'summary_metrics' => $summaryMetrics,
            'triage_review_progress' => $triageReviewProgress['families'],
            'attention_items' => $attentionItems,
            'attention_empty_state' => $attentionEmptyState,
            'recent_operations' => $recentOperations,
            'recent_operations_empty_state' => [
                'title' => 'No recent operations yet',
                'body' => 'Diagnostic execution context will appear here once runs start. This section does not define workspace health on its own.',
                'action_label' => 'Open operations',
                'action_url' => OperationRunLinks::index(context: $navigationContext, allTenants: true),
            ],
            'quick_actions' => $quickActions,
            'zero_tenant_state' => $zeroTenantState,
            'calmness' => $calmness,
        ];
    }

    /**
     * @return Collection<int, ManagedEnvironment>
     */
    private function accessibleTenants(Workspace $workspace, User $user): Collection
    {
        $query = ManagedEnvironment::query()
            ->where('workspace_id', (int) $workspace->getKey())
            ->where('lifecycle_status', ManagedEnvironment::STATUS_ACTIVE);

        $this->managedEnvironmentAccessScopeResolver->applyWorkspaceScopeToQuery(
            query: $query,
            user: $user,
            workspaceId: (int) $workspace->getKey(),
            qualifiedEnvironmentColumn: 'managed_environments.id',
        );

        return $query
            ->orderBy('name')
            ->get(['id', 'name', 'slug', 'workspace_id']);
    }

    /**
     * @param  array<int, int>  $visibleTenantIds
     * @return array<string, mixed>
     */
    private function myFindingsSignal(int $workspaceId, array $visibleTenantIds, User $user): array
    {
        $assignedCounts = $visibleTenantIds === []
            ? null
            : $this->scopeToVisibleTenants(
                Finding::query(),
                $workspaceId,
                $visibleTenantIds,
            )
                ->where('assignee_user_id', (int) $user->getKey())
                ->whereIn('status', Finding::openStatusesForQuery())
                ->selectRaw('count(*) as open_assigned_count')
                ->selectRaw('sum(case when due_at is not null and due_at < ? then 1 else 0 end) as overdue_assigned_count', [now()])
                ->first();

        $openAssignedCount = is_numeric($assignedCounts?->open_assigned_count)
            ? (int) $assignedCounts->open_assigned_count
            : 0;

        $overdueAssignedCount = is_numeric($assignedCounts?->overdue_assigned_count)
            ? (int) $assignedCounts->overdue_assigned_count
            : 0;

        $isCalm = $openAssignedCount === 0;

        return [
            'headline' => $isCalm
                ? 'Assigned work is calm'
                : sprintf(
                    '%d assigned %s visible in this workspace',
                    $openAssignedCount,
                    Str::plural('finding', $openAssignedCount),
                ),
            'description' => $isCalm
                ? 'No visible assigned findings currently need attention across your entitled environments.'
                : sprintf(
                    'Visible assigned work stays in one queue. %d overdue %s currently need follow-up.',
                    $overdueAssignedCount,
                    Str::plural('finding', $overdueAssignedCount),
                ),
            'open_assigned_count' => $openAssignedCount,
            'overdue_assigned_count' => $overdueAssignedCount,
            'is_calm' => $isCalm,
            'cta_label' => 'Open my findings',
            'cta_url' => MyFindingsInbox::getUrl(panel: 'admin'),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function findingsHygieneSignal(Workspace $workspace, array $visibleTenantIds): array
    {
        $summary = $this->findingAssignmentHygieneService->summaryForVisibleTenantIds($workspace, $visibleTenantIds);
        $uniqueIssueCount = $summary['unique_issue_count'];
        $brokenAssignmentCount = $summary['broken_assignment_count'];
        $staleInProgressCount = $summary['stale_in_progress_count'];
        $isCalm = $uniqueIssueCount === 0;

        return [
            'headline' => $isCalm
                ? 'Findings hygiene is calm'
                : sprintf(
                    '%d visible hygiene %s need follow-up',
                    $uniqueIssueCount,
                    Str::plural('issue', $uniqueIssueCount),
                ),
            'description' => $this->findingsHygieneDescription($brokenAssignmentCount, $staleInProgressCount),
            'unique_issue_count' => $uniqueIssueCount,
            'broken_assignment_count' => $brokenAssignmentCount,
            'stale_in_progress_count' => $staleInProgressCount,
            'is_calm' => $isCalm,
            'cta_label' => 'Open hygiene report',
            'cta_url' => FindingsHygieneReport::getUrl(panel: 'admin'),
        ];
    }

    /**
     * @param  Collection<int, ManagedEnvironment>  $accessibleTenants
     * @return array<int, int>
     */
    private function visibleFindingTenantIds(Collection $accessibleTenants, User $user): array
    {
        return $accessibleTenants
            ->filter(fn (ManagedEnvironment $tenant): bool => $this->capabilityResolver->can($user, $tenant, Capabilities::TENANT_FINDINGS_VIEW))
            ->pluck('id')
            ->map(static fn (mixed $id): int => (int) $id)
            ->values()
            ->all();
    }

    private function findingsHygieneDescription(int $brokenAssignmentCount, int $staleInProgressCount): string
    {
        if ($brokenAssignmentCount === 0 && $staleInProgressCount === 0) {
            return 'No broken assignments or stale in-progress work are visible across your entitled environments.';
        }

        if ($brokenAssignmentCount > 0 && $staleInProgressCount > 0) {
            return sprintf(
                '%d broken %s and %d stale in-progress %s need repair.',
                $brokenAssignmentCount,
                Str::plural('assignment', $brokenAssignmentCount),
                $staleInProgressCount,
                Str::plural('finding', $staleInProgressCount),
            );
        }

        if ($brokenAssignmentCount > 0) {
            return sprintf(
                '%d broken %s need repair before work can continue.',
                $brokenAssignmentCount,
                Str::plural('assignment', $brokenAssignmentCount),
            );
        }

        return sprintf(
            '%d stale in-progress %s need follow-up.',
            $staleInProgressCount,
            Str::plural('finding', $staleInProgressCount),
        );
    }

    /**
     * @param  Collection<int, ManagedEnvironment>  $accessibleTenants
     * @return list<array<string, mixed>>
     */
    private function tenantContexts(Collection $accessibleTenants, int $workspaceId, bool $canViewAlerts): array
    {
        $accessibleTenantIds = $accessibleTenants
            ->pluck('id')
            ->map(static fn (mixed $id): int => (int) $id)
            ->all();

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

        $backupHealthByTenant = $this->tenantBackupHealthResolver->assessMany($accessibleTenantIds);
        $recoveryEvidenceByTenant = $this->restoreSafetyResolver->dashboardRecoveryEvidenceForTenants(
            $accessibleTenantIds,
            $backupHealthByTenant,
        );

        $terminalFollowUpCounts = $this->scopeToVisibleTenants(
            OperationRun::query(),
            $workspaceId,
            $accessibleTenantIds,
        )
            ->terminalFollowUp()
            ->selectRaw('managed_environment_id, count(*) as aggregate_count')
            ->groupBy('managed_environment_id')
            ->pluck('aggregate_count', 'managed_environment_id')
            ->map(static fn (mixed $count): int => (int) $count)
            ->all();

        $staleAttentionCounts = $this->scopeToVisibleTenants(
            OperationRun::query(),
            $workspaceId,
            $accessibleTenantIds,
        )
            ->activeStaleAttention()
            ->selectRaw('managed_environment_id, count(*) as aggregate_count')
            ->groupBy('managed_environment_id')
            ->pluck('aggregate_count', 'managed_environment_id')
            ->map(static fn (mixed $count): int => (int) $count)
            ->all();

        $activeOperationCounts = $this->scopeToVisibleTenants(
            OperationRun::query(),
            $workspaceId,
            $accessibleTenantIds,
        )
            ->healthyActive()
            ->selectRaw('managed_environment_id, count(*) as aggregate_count')
            ->groupBy('managed_environment_id')
            ->pluck('aggregate_count', 'managed_environment_id')
            ->map(static fn (mixed $count): int => (int) $count)
            ->all();

        $alertFailureCounts = $canViewAlerts
            ? $this->scopeToVisibleTenants(
                AlertDelivery::query(),
                $workspaceId,
                $accessibleTenantIds,
            )
                ->where('created_at', '>=', now()->subDays(7))
                ->where('status', AlertDelivery::STATUS_FAILED)
                ->selectRaw('managed_environment_id, count(*) as aggregate_count')
                ->groupBy('managed_environment_id')
                ->pluck('aggregate_count', 'managed_environment_id')
                ->map(static fn (mixed $count): int => (int) $count)
                ->all()
            : [];

        return $accessibleTenants
            ->map(function (ManagedEnvironment $tenant) use (
                $terminalFollowUpCounts,
                $staleAttentionCounts,
                $activeOperationCounts,
                $alertFailureCounts,
                $backupHealthByTenant,
                $recoveryEvidenceByTenant,
            ): array {
                $tenantId = (int) $tenant->getKey();
                $aggregate = $this->governanceAggregate($tenant);
                $backupHealth = $backupHealthByTenant[$tenantId] ?? null;
                $recoveryEvidence = $recoveryEvidenceByTenant[$tenantId] ?? null;
                $recoveryOverviewState = is_array($recoveryEvidence)
                    ? ($recoveryEvidence['overview_state'] ?? null)
                    : null;
                $hasBackupAttention = $backupHealth instanceof TenantBackupHealthAssessment
                    && in_array($backupHealth->posture, [
                        TenantBackupHealthAssessment::POSTURE_ABSENT,
                        TenantBackupHealthAssessment::POSTURE_STALE,
                        TenantBackupHealthAssessment::POSTURE_DEGRADED,
                    ], true);

                return [
                    'tenant' => $tenant,
                    'aggregate' => $aggregate,
                    'backup_health_assessment' => $backupHealth,
                    'backup_health_posture' => $backupHealth instanceof TenantBackupHealthAssessment
                        ? $backupHealth->posture
                        : null,
                    'backup_health_reason' => $backupHealth instanceof TenantBackupHealthAssessment
                        ? $backupHealth->primaryReason
                        : null,
                    'backup_health_headline' => $backupHealth instanceof TenantBackupHealthAssessment
                        ? $backupHealth->headline
                        : null,
                    'backup_health_summary' => $backupHealth instanceof TenantBackupHealthAssessment
                        ? $backupHealth->supportingMessage
                        : null,
                    'backup_health_boundary' => $backupHealth instanceof TenantBackupHealthAssessment
                        ? $backupHealth->positiveClaimBoundary
                        : null,
                    'has_backup_attention' => $hasBackupAttention,
                    'recovery_evidence' => $recoveryEvidence,
                    'recovery_evidence_state' => is_string($recoveryOverviewState) ? $recoveryOverviewState : null,
                    'recovery_evidence_reason' => is_array($recoveryEvidence) && is_string($recoveryEvidence['reason'] ?? null)
                        ? $recoveryEvidence['reason']
                        : null,
                    'recovery_evidence_headline' => is_array($recoveryEvidence) && is_string($recoveryEvidence['headline'] ?? null)
                        ? $recoveryEvidence['headline']
                        : null,
                    'recovery_evidence_summary' => is_array($recoveryEvidence) && is_string($recoveryEvidence['summary'] ?? null)
                        ? $recoveryEvidence['summary']
                        : null,
                    'recovery_evidence_boundary' => is_array($recoveryEvidence) && is_string($recoveryEvidence['claim_boundary'] ?? null)
                        ? $recoveryEvidence['claim_boundary']
                        : null,
                    'latest_relevant_restore_run_id' => is_array($recoveryEvidence) && is_numeric($recoveryEvidence['latest_relevant_restore_run_id'] ?? null)
                        ? (int) $recoveryEvidence['latest_relevant_restore_run_id']
                        : null,
                    'has_recovery_attention' => in_array($recoveryOverviewState, ['weakened', 'unvalidated'], true),
                    'has_governance_attention' => $this->hasGovernanceAttention($aggregate),
                    'terminal_follow_up_operations_count' => (int) ($terminalFollowUpCounts[$tenantId] ?? 0),
                    'stale_attention_operations_count' => (int) ($staleAttentionCounts[$tenantId] ?? 0),
                    'active_operations_count' => (int) ($activeOperationCounts[$tenantId] ?? 0),
                    'alert_failures_count' => (int) ($alertFailureCounts[$tenantId] ?? 0),
                ];
            })
            ->all();
    }

    private function governanceAggregate(ManagedEnvironment $tenant): TenantGovernanceAggregate
    {
        /** @var TenantGovernanceAggregate $aggregate */
        $aggregate = $this->tenantGovernanceAggregateResolver->forTenant($tenant);

        return $aggregate;
    }

    private function hasGovernanceAttention(TenantGovernanceAggregate $aggregate): bool
    {
        if (
            $aggregate->lapsedGovernanceCount > 0
            || $aggregate->overdueOpenFindingsCount > 0
            || $aggregate->expiringGovernanceCount > 0
            || $aggregate->highSeverityActiveFindingsCount > 0
        ) {
            return true;
        }

        return $this->shouldPromoteCompareAttention($aggregate);
    }

    private function shouldPromoteCompareAttention(TenantGovernanceAggregate $aggregate): bool
    {
        if ($aggregate->summaryAssessment->stateFamily === BaselineCompareSummaryAssessment::STATE_STALE) {
            return true;
        }

        return $aggregate->summaryAssessment->stateFamily === BaselineCompareSummaryAssessment::STATE_ACTION_REQUIRED
            && in_array($aggregate->nextActionTarget, [
                BaselineCompareSummaryAssessment::NEXT_TARGET_LANDING,
                BaselineCompareSummaryAssessment::NEXT_TARGET_RUN,
            ], true);
    }

    /**
     * @param  list<array<string, mixed>>  $tenantContexts
     * @return list<array<string, mixed>>
     */
    private function attentionItems(
        array $tenantContexts,
        User $user,
        bool $canViewAlerts,
        CanonicalNavigationContext $navigationContext,
    ): array {
        $items = collect($tenantContexts)
            ->flatMap(function (array $context) use ($user, $canViewAlerts, $navigationContext): array {
                $tenant = $context['tenant'] ?? null;
                $aggregate = $context['aggregate'] ?? null;

                if (! $tenant instanceof ManagedEnvironment || ! $aggregate instanceof TenantGovernanceAggregate) {
                    return [];
                }

                if ($aggregate->lapsedGovernanceCount > 0) {
                    return [$this->makeAttentionItem(
                        tenant: $tenant,
                        key: 'tenant_lapsed_governance',
                        family: 'governance',
                        urgency: 'critical',
                        title: 'Lapsed accepted-risk governance',
                        body: sprintf(
                            '%d accepted-risk finding%s no longer have valid governance backing.',
                            $aggregate->lapsedGovernanceCount,
                            $aggregate->lapsedGovernanceCount === 1 ? '' : 's',
                        ),
                        badge: 'Governance',
                        badgeColor: 'danger',
                        destination: $this->tenantDashboardTarget($tenant, $user, 'Open environment'),
                        supportingMessage: 'Open the environment dashboard to review the full invalid-governance family without narrowing the findings set.',
                    )];
                }

                if ($aggregate->overdueOpenFindingsCount > 0) {
                    return [$this->makeAttentionItem(
                        tenant: $tenant,
                        key: 'tenant_overdue_findings',
                        family: 'findings',
                        urgency: 'critical',
                        title: 'Overdue findings',
                        body: sprintf(
                            '%d open finding%s are overdue and still need workflow follow-up.',
                            $aggregate->overdueOpenFindingsCount,
                            $aggregate->overdueOpenFindingsCount === 1 ? '' : 's',
                        ),
                        badge: 'Findings',
                        badgeColor: 'danger',
                        destination: $this->findingsTarget(
                            tenant: $tenant,
                            user: $user,
                            filters: ['tab' => 'overdue'],
                        ),
                    )];
                }

                if ($this->shouldPromoteCompareAttention($aggregate)) {
                    return [$this->makeAttentionItem(
                        tenant: $tenant,
                        key: 'tenant_compare_attention',
                        family: 'compare',
                        urgency: $aggregate->summaryAssessment->stateFamily === BaselineCompareSummaryAssessment::STATE_STALE ? 'high' : 'critical',
                        title: 'Baseline compare posture',
                        body: $aggregate->headline,
                        badge: 'Baseline',
                        badgeColor: $aggregate->tone,
                        destination: $this->baselineCompareTarget($tenant, $user),
                        supportingMessage: $aggregate->supportingMessage,
                    )];
                }

                if ($aggregate->highSeverityActiveFindingsCount > 0) {
                    return [$this->makeAttentionItem(
                        tenant: $tenant,
                        key: 'tenant_high_severity_findings',
                        family: 'findings',
                        urgency: 'high',
                        title: 'High severity active findings',
                        body: sprintf(
                            '%d high or critical finding%s are still active.',
                            $aggregate->highSeverityActiveFindingsCount,
                            $aggregate->highSeverityActiveFindingsCount === 1 ? '' : 's',
                        ),
                        badge: 'Findings',
                        badgeColor: 'danger',
                        destination: $this->findingsTarget(
                            tenant: $tenant,
                            user: $user,
                            filters: ['tab' => 'needs_action', 'high_severity' => true],
                        ),
                    )];
                }

                if ($aggregate->expiringGovernanceCount > 0) {
                    return [$this->makeAttentionItem(
                        tenant: $tenant,
                        key: 'tenant_expiring_governance',
                        family: 'governance',
                        urgency: 'medium',
                        title: 'Expiring accepted-risk governance',
                        body: sprintf(
                            '%d accepted-risk finding%s need governance review soon.',
                            $aggregate->expiringGovernanceCount,
                            $aggregate->expiringGovernanceCount === 1 ? '' : 's',
                        ),
                        badge: 'Governance',
                        badgeColor: 'warning',
                        destination: $this->findingsTarget(
                            tenant: $tenant,
                            user: $user,
                            filters: [
                                'tab' => 'risk_accepted',
                                'governance_validity' => FindingException::VALIDITY_EXPIRING,
                            ],
                        ),
                    )];
                }

                if (($backupHealthItem = $this->backupHealthAttentionItem($tenant, $context, $user)) !== null) {
                    return [$backupHealthItem];
                }

                if (($recoveryEvidenceItem = $this->recoveryEvidenceAttentionItem($tenant, $context, $user)) !== null) {
                    return [$recoveryEvidenceItem];
                }

                $items = [];

                $terminalFollowUpOperationsCount = (int) ($context['terminal_follow_up_operations_count'] ?? 0);

                if ($terminalFollowUpOperationsCount > 0) {
                    $items[] = $this->makeAttentionItem(
                        tenant: $tenant,
                        key: 'tenant_operations_terminal_follow_up',
                        family: 'operations',
                        urgency: 'medium',
                        title: 'Terminal operations need follow-up',
                        body: sprintf(
                            '%d run%s finished blocked, partially, failed, or were automatically reconciled.',
                            $terminalFollowUpOperationsCount,
                            $terminalFollowUpOperationsCount === 1 ? '' : 's',
                        ),
                        badge: 'Operations',
                        badgeColor: 'danger',
                        destination: $this->operationsIndexTarget(
                            $tenant,
                            $navigationContext,
                            OperationRun::PROBLEM_CLASS_TERMINAL_FOLLOW_UP,
                            OperationRun::PROBLEM_CLASS_TERMINAL_FOLLOW_UP,
                            'Open terminal follow-up',
                        ),
                    );
                }

                $staleAttentionOperationsCount = (int) ($context['stale_attention_operations_count'] ?? 0);

                if ($staleAttentionOperationsCount > 0) {
                    $items[] = $this->makeAttentionItem(
                        tenant: $tenant,
                        key: 'tenant_operations_stale_attention',
                        family: 'operations',
                        urgency: 'medium',
                        title: 'Active operations look stale',
                        body: sprintf(
                            '%d run%s are still marked active but are past the lifecycle window.',
                            $staleAttentionOperationsCount,
                            $staleAttentionOperationsCount === 1 ? '' : 's',
                        ),
                        badge: 'Operations',
                        badgeColor: 'warning',
                        destination: $this->operationsIndexTarget(
                            $tenant,
                            $navigationContext,
                            OperationRun::PROBLEM_CLASS_ACTIVE_STALE_ATTENTION,
                            OperationRun::PROBLEM_CLASS_ACTIVE_STALE_ATTENTION,
                            'Open stale operations',
                        ),
                    );
                }

                if ($items !== []) {
                    return $items;
                }

                $activeOperationsCount = (int) ($context['active_operations_count'] ?? 0);

                if ($activeOperationsCount > 0) {
                    return [$this->makeAttentionItem(
                        tenant: $tenant,
                        key: 'tenant_active_operations',
                        family: 'operations',
                        urgency: 'supporting',
                        title: 'Operations are running',
                        body: sprintf(
                            '%d run%s are currently queued or in progress for this environment.',
                            $activeOperationsCount,
                            $activeOperationsCount === 1 ? '' : 's',
                        ),
                        badge: 'Operations',
                        badgeColor: 'warning',
                        destination: $this->operationsIndexTarget($tenant, $navigationContext, 'active'),
                    )];
                }

                $alertFailuresCount = (int) ($context['alert_failures_count'] ?? 0);

                if ($canViewAlerts && $alertFailuresCount > 0) {
                    return [$this->makeAttentionItem(
                        tenant: $tenant,
                        key: 'tenant_alert_delivery_failures',
                        family: 'alerts',
                        urgency: 'supporting',
                        title: 'Alert deliveries failed',
                        body: sprintf(
                            '%d alert delivery attempt%s failed in the last 7 days for this environment.',
                            $alertFailuresCount,
                            $alertFailuresCount === 1 ? '' : 's',
                        ),
                        badge: 'Alerts',
                        badgeColor: 'danger',
                        destination: $this->alertsOverviewTarget($navigationContext, true),
                    )];
                }

                return [];
            })
            ->values()
            ->all();

        usort($items, function (array $left, array $right): int {
            $leftPriority = $this->attentionPriority($left);
            $rightPriority = $this->attentionPriority($right);

            if ($leftPriority !== $rightPriority) {
                return $rightPriority <=> $leftPriority;
            }

            return strcmp((string) ($left['tenant_label'] ?? ''), (string) ($right['tenant_label'] ?? ''));
        });

        return array_slice($items, 0, 5);
    }

    private function attentionPriority(array $item): int
    {
        return match ($item['key'] ?? null) {
            'tenant_lapsed_governance' => 100,
            'tenant_overdue_findings' => 95,
            'tenant_compare_attention' => 90,
            'tenant_high_severity_findings' => 80,
            'tenant_expiring_governance' => 70,
            'tenant_backup_absent' => 65,
            'tenant_recovery_weakened' => 60,
            'tenant_backup_stale' => 58,
            'tenant_recovery_unvalidated' => 55,
            'tenant_backup_degraded' => 52,
            'tenant_operations_stale_attention' => 45,
            'tenant_operations_terminal_follow_up' => 40,
            'tenant_active_operations' => 20,
            'tenant_alert_delivery_failures' => 10,
            default => 0,
        };
    }

    /**
     * @param  array<string, mixed>  $context
     * @return array<string, mixed>|null
     */
    private function backupHealthAttentionItem(ManagedEnvironment $tenant, array $context, User $user): ?array
    {
        $assessment = $context['backup_health_assessment'] ?? null;

        if (! $assessment instanceof TenantBackupHealthAssessment) {
            return null;
        }

        if (! in_array($assessment->posture, [
            TenantBackupHealthAssessment::POSTURE_ABSENT,
            TenantBackupHealthAssessment::POSTURE_STALE,
            TenantBackupHealthAssessment::POSTURE_DEGRADED,
        ], true)) {
            return null;
        }

        $reasonContext = [
            'family' => 'backup_health',
            'state' => $assessment->posture,
            'reason' => $assessment->primaryReason,
        ];

        return $this->makeAttentionItem(
            tenant: $tenant,
            key: match ($assessment->posture) {
                TenantBackupHealthAssessment::POSTURE_ABSENT => 'tenant_backup_absent',
                TenantBackupHealthAssessment::POSTURE_STALE => 'tenant_backup_stale',
                default => 'tenant_backup_degraded',
            },
            family: 'backup_health',
            urgency: match ($assessment->posture) {
                TenantBackupHealthAssessment::POSTURE_ABSENT => 'critical',
                TenantBackupHealthAssessment::POSTURE_STALE => 'high',
                default => 'medium',
            },
            title: $this->backupHealthAttentionTitle($assessment),
            body: $assessment->supportingMessage ?? $assessment->headline,
            badge: 'Backup health',
            badgeColor: $assessment->tone(),
            destination: $this->tenantDashboardTarget(
                $tenant,
                $user,
                arrivalState: $this->workspaceOverviewArrivalState($tenant, $reasonContext),
            ),
            supportingMessage: $assessment->positiveClaimBoundary,
            reasonContext: $reasonContext,
        );
    }

    /**
     * @param  array<string, mixed>  $context
     * @return array<string, mixed>|null
     */
    private function recoveryEvidenceAttentionItem(ManagedEnvironment $tenant, array $context, User $user): ?array
    {
        $recoveryEvidence = $context['recovery_evidence'] ?? null;

        if (! is_array($recoveryEvidence)) {
            return null;
        }

        $overviewState = is_string($recoveryEvidence['overview_state'] ?? null)
            ? $recoveryEvidence['overview_state']
            : null;

        if (! in_array($overviewState, ['weakened', 'unvalidated'], true)) {
            return null;
        }

        $latestRelevantAttention = $recoveryEvidence['latest_relevant_attention'] ?? null;
        $attentionState = $latestRelevantAttention instanceof RestoreResultAttention
            ? $latestRelevantAttention->state
            : (is_string($recoveryEvidence['latest_relevant_attention_state'] ?? null)
                ? $recoveryEvidence['latest_relevant_attention_state']
                : null);
        $supportingParts = [
            $latestRelevantAttention instanceof RestoreResultAttention
                ? RestoreSafetyCopy::primaryNextAction($latestRelevantAttention->primaryNextAction)
                : null,
            is_string($recoveryEvidence['claim_boundary'] ?? null) ? $recoveryEvidence['claim_boundary'] : null,
        ];

        return $this->makeAttentionItem(
            tenant: $tenant,
            key: $overviewState === 'weakened'
                ? 'tenant_recovery_weakened'
                : 'tenant_recovery_unvalidated',
            family: 'recovery_evidence',
            urgency: $overviewState === 'weakened' ? 'high' : 'medium',
            title: $overviewState === 'unvalidated'
                ? 'Recovery evidence is unvalidated'
                : $this->recoveryAttentionTitle($attentionState),
            body: (string) ($recoveryEvidence['summary'] ?? 'Recent restore history weakens confidence.'),
            badge: 'Recovery evidence',
            badgeColor: $overviewState === 'weakened' && $attentionState === RestoreResultAttention::STATE_FAILED
                ? 'danger'
                : 'warning',
            destination: $this->tenantDashboardTarget(
                $tenant,
                $user,
                arrivalState: $this->workspaceOverviewArrivalState($tenant, [
                    'family' => 'recovery_evidence',
                    'state' => $overviewState,
                    'reason' => is_string($recoveryEvidence['reason'] ?? null) ? $recoveryEvidence['reason'] : null,
                ]),
            ),
            supportingMessage: trim(implode(' ', array_filter($supportingParts, static fn (?string $part): bool => filled($part)))),
            reasonContext: [
                'family' => 'recovery_evidence',
                'state' => $overviewState,
                'reason' => is_string($recoveryEvidence['reason'] ?? null) ? $recoveryEvidence['reason'] : null,
            ],
        );
    }

    private function backupHealthAttentionTitle(TenantBackupHealthAssessment $assessment): string
    {
        return match ($assessment->primaryReason) {
            TenantBackupHealthAssessment::REASON_NO_BACKUP_BASIS => 'No usable backup basis',
            TenantBackupHealthAssessment::REASON_LATEST_BACKUP_STALE => 'Latest backup is stale',
            TenantBackupHealthAssessment::REASON_LATEST_BACKUP_DEGRADED => 'Latest backup is degraded',
            default => $assessment->headline,
        };
    }

    private function recoveryAttentionTitle(?string $attentionState): string
    {
        return match ($attentionState) {
            RestoreResultAttention::STATE_FAILED => 'Recent restore failed',
            RestoreResultAttention::STATE_PARTIAL => 'Recent restore is partial',
            RestoreResultAttention::STATE_COMPLETED_WITH_FOLLOW_UP => 'Recent restore needs follow-up',
            default => 'Recent restore history weakens confidence',
        };
    }

    /**
     * @return array<string, mixed>
     */
    private function makeAttentionItem(
        ManagedEnvironment $tenant,
        string $key,
        string $family,
        string $urgency,
        string $title,
        string $body,
        string $badge,
        string $badgeColor,
        array $destination,
        ?string $supportingMessage = null,
        ?array $reasonContext = null,
    ): array {
        $item = [
            'key' => $key,
            'managed_environment_id' => (int) $tenant->getKey(),
            'tenant_label' => (string) $tenant->name,
            'tenant_route_key' => $this->tenantRouteKey($tenant),
            'family' => $family,
            'urgency' => $urgency,
            'title' => $title,
            'body' => $body,
            'supporting_message' => $supportingMessage,
            'badge' => $badge,
            'badge_color' => $badgeColor,
            'reason_context' => $reasonContext,
            'destination' => $destination,
            'action_disabled' => (bool) ($destination['disabled'] ?? false),
            'helper_text' => $destination['helper_text'] ?? null,
        ];

        $item['url'] = $destination['disabled'] === true ? null : ($destination['url'] ?? null);

        return $item;
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function summaryMetrics(
        int $accessibleTenantCount,
        int $governanceAttentionTenantCount,
        int $backupAttentionTenantCount,
        int $recoveryAttentionTenantCount,
        int $totalActiveOperationsCount,
        int $totalAlertFailuresCount,
        bool $canViewAlerts,
        array $tenantContexts,
        array $triageReviewSummaries,
        User $user,
        CanonicalNavigationContext $navigationContext,
    ): array {
        $metrics = [
            $this->makeSummaryMetric(
                key: 'accessible_tenants',
                label: 'Accessible environments',
                value: $accessibleTenantCount,
                category: 'scope',
                description: $accessibleTenantCount > 0
                    ? 'Environment drill-down stays explicit from this workspace home.'
                    : 'No managed environments are available in this workspace yet.',
                color: $accessibleTenantCount > 0 ? 'primary' : 'warning',
                destination: $accessibleTenantCount > 0
                    ? $this->chooseEnvironmentTarget()
                    : $this->switchWorkspaceTarget(),
            ),
            $this->makeSummaryMetric(
                key: 'governance_attention_tenants',
                label: 'Governance attention',
                value: $governanceAttentionTenantCount,
                category: 'governance_risk',
                description: 'Affected visible environments with overdue findings, governance expiry, lapsed governance, or compare posture that needs review.',
                color: $governanceAttentionTenantCount > 0 ? 'danger' : 'gray',
                destination: $governanceAttentionTenantCount > 0
                    ? $this->chooseEnvironmentTarget(__('localization.shell.choose_environment'))
                    : null,
            ),
            $this->makeSummaryMetric(
                key: 'backup_attention_tenants',
                label: 'Backup attention',
                value: $backupAttentionTenantCount,
                category: 'backup_health',
                description: $this->reviewSummaryMetricDescription(
                    family: PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
                    baseDescription: 'Visible environments with non-healthy backup posture.',
                    triageReviewSummaries: $triageReviewSummaries,
                ),
                color: $backupAttentionTenantCount > 0 ? 'danger' : 'gray',
                destination: $this->attentionMetricDestination(
                    tenantContexts: $tenantContexts,
                    user: $user,
                    stateKey: 'has_backup_attention',
                ),
            ),
            $this->makeSummaryMetric(
                key: 'recovery_attention_tenants',
                label: 'Recovery attention',
                value: $recoveryAttentionTenantCount,
                category: 'recovery_evidence',
                description: $this->reviewSummaryMetricDescription(
                    family: PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE,
                    baseDescription: 'Visible environments with weakened or unvalidated recovery evidence.',
                    triageReviewSummaries: $triageReviewSummaries,
                ),
                color: $recoveryAttentionTenantCount > 0 ? 'warning' : 'gray',
                destination: $this->attentionMetricDestination(
                    tenantContexts: $tenantContexts,
                    user: $user,
                    stateKey: 'has_recovery_attention',
                ),
            ),
            $this->makeSummaryMetric(
                key: 'active_operations',
                label: 'Active operations',
                value: $totalActiveOperationsCount,
                category: 'activity',
                description: 'Activity only. Active execution does not imply governance health.',
                color: $totalActiveOperationsCount > 0 ? 'warning' : 'gray',
                destination: $totalActiveOperationsCount > 0
                    ? $this->operationsIndexTarget(null, $navigationContext, 'active')
                    : null,
            ),
        ];

        if ($canViewAlerts) {
            $metrics[] = $this->makeSummaryMetric(
                key: 'alert_failures',
                label: 'Alert failures (7d)',
                value: $totalAlertFailuresCount,
                category: 'alerts',
                description: 'Alert delivery follow-up for the visible workspace slice in the last 7 days.',
                color: $totalAlertFailuresCount > 0 ? 'danger' : 'gray',
                destination: $totalAlertFailuresCount > 0
                    ? $this->alertsOverviewTarget($navigationContext, true)
                    : null,
            );
        }

        return $metrics;
    }

    /**
     * @param  list<array<string, mixed>>  $tenantContexts
     * @return array{
     *     summaries: array<string, array{
     *         concern_family: string,
     *         affected_total: int,
     *         reviewed_count: int,
     *         follow_up_needed_count: int,
     *         changed_since_review_count: int,
     *         not_reviewed_count: int
     *     }>,
     *     families: list<array<string, mixed>>
     * }
     */
    private function triageReviewProgress(int $workspaceId, array $tenantContexts): array
    {
        $tenantIds = [];
        $backupHealthByTenant = [];
        $recoveryEvidenceByTenant = [];

        foreach ($tenantContexts as $context) {
            $tenant = $context['tenant'] ?? null;

            if (! $tenant instanceof ManagedEnvironment) {
                continue;
            }

            $tenantId = (int) $tenant->getKey();
            $tenantIds[] = $tenantId;
            $backupHealthByTenant[$tenantId] = $context['backup_health_assessment'] ?? null;
            $recoveryEvidenceByTenant[$tenantId] = $context['recovery_evidence'] ?? null;
        }

        $resolved = $this->managedEnvironmentTriageReviewStateResolver->resolveMany(
            workspaceId: $workspaceId,
            tenantIds: $tenantIds,
            backupHealthByTenant: $backupHealthByTenant,
            recoveryEvidenceByTenant: $recoveryEvidenceByTenant,
        );

        return [
            'summaries' => $resolved['summaries'],
            'families' => $this->triageReviewProgressFamilies($resolved['summaries']),
        ];
    }

    /**
     * @param  array<string, array{
     *     concern_family: string,
     *     affected_total: int,
     *     reviewed_count: int,
     *     follow_up_needed_count: int,
     *     changed_since_review_count: int,
     *     not_reviewed_count: int
     * }>  $triageReviewSummaries
     */
    private function reviewSummaryMetricDescription(
        string $family,
        string $baseDescription,
        array $triageReviewSummaries,
    ): string {
        $summary = $triageReviewSummaries[$family] ?? null;

        if (! is_array($summary) || (int) ($summary['affected_total'] ?? 0) === 0) {
            return $baseDescription;
        }

        return sprintf(
            '%s Reviewed %d/%d. Follow-up needed %d. Changed since review %d.',
            $baseDescription,
            (int) ($summary['reviewed_count'] ?? 0),
            (int) ($summary['affected_total'] ?? 0),
            (int) ($summary['follow_up_needed_count'] ?? 0),
            (int) ($summary['changed_since_review_count'] ?? 0),
        );
    }

    /**
     * @return array<string, mixed>
     */
    private function makeSummaryMetric(
        string $key,
        string $label,
        int $value,
        string $category,
        string $description,
        string $color,
        ?array $destination,
    ): array {
        return [
            'key' => $key,
            'label' => $label,
            'value' => $value,
            'category' => $category,
            'description' => $description,
            'color' => $color,
            'destination' => $destination,
            'destination_url' => $destination !== null && ($destination['disabled'] ?? false) === false
                ? ($destination['url'] ?? null)
                : null,
        ];
    }

    /**
     * @param  array<string, array{
     *     concern_family: string,
     *     affected_total: int,
     *     reviewed_count: int,
     *     follow_up_needed_count: int,
     *     changed_since_review_count: int,
     *     not_reviewed_count: int
     * }>  $triageReviewSummaries
     * @return list<array<string, mixed>>
     */
    private function triageReviewProgressFamilies(array $triageReviewSummaries): array
    {
        $families = [];

        foreach ([PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH, PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE] as $family) {
            $summary = $triageReviewSummaries[$family] ?? null;

            if (! is_array($summary) || (int) ($summary['affected_total'] ?? 0) === 0) {
                continue;
            }

            $families[] = [
                'concern_family' => $family,
                'label' => $family === PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH ? 'Backup health' : 'Recovery evidence',
                'affected_total' => (int) ($summary['affected_total'] ?? 0),
                'reviewed_count' => (int) ($summary['reviewed_count'] ?? 0),
                'follow_up_needed_count' => (int) ($summary['follow_up_needed_count'] ?? 0),
                'changed_since_review_count' => (int) ($summary['changed_since_review_count'] ?? 0),
                'not_reviewed_count' => (int) ($summary['not_reviewed_count'] ?? 0),
                'reviewed_destination' => $this->triageReviewBucketDestination($family, ManagedEnvironmentTriageReview::STATE_REVIEWED, 'Reviewed'),
                'follow_up_needed_destination' => $this->triageReviewBucketDestination($family, ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED, 'Follow-up needed'),
                'changed_since_review_destination' => $this->triageReviewBucketDestination($family, ManagedEnvironmentTriageReview::DERIVED_STATE_CHANGED_SINCE_REVIEW, 'Changed since review'),
                'not_reviewed_destination' => $this->triageReviewBucketDestination($family, ManagedEnvironmentTriageReview::DERIVED_STATE_NOT_REVIEWED, 'Not reviewed'),
            ];
        }

        return $families;
    }

    /**
     * @return array<string, mixed>
     */
    private function triageReviewBucketDestination(string $family, string $reviewState, string $label): array
    {
        $filters = match ($family) {
            PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH => [
                'backup_posture' => [
                    TenantBackupHealthAssessment::POSTURE_ABSENT,
                    TenantBackupHealthAssessment::POSTURE_STALE,
                    TenantBackupHealthAssessment::POSTURE_DEGRADED,
                ],
                'review_state' => [$reviewState],
                'triage_sort' => TenantRecoveryTriagePresentation::TRIAGE_SORT_WORST_FIRST,
            ],
            PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE => [
                'recovery_evidence' => [
                    TenantRecoveryTriagePresentation::RECOVERY_EVIDENCE_WEAKENED,
                    TenantRecoveryTriagePresentation::RECOVERY_EVIDENCE_UNVALIDATED,
                ],
                'review_state' => [$reviewState],
                'triage_sort' => TenantRecoveryTriagePresentation::TRIAGE_SORT_WORST_FIRST,
            ],
            default => [
                'review_state' => [$reviewState],
            ],
        };

        return $this->filteredTenantRegistryTarget($filters, $label);
    }

    /**
     * @param  list<array<string, mixed>>  $tenantContexts
     */
    private function attentionMetricDestination(array $tenantContexts, User $user, string $stateKey): ?array
    {
        $affectedContexts = array_values(array_filter(
            $tenantContexts,
            static fn (array $context): bool => (bool) ($context[$stateKey] ?? false),
        ));

        if ($affectedContexts === []) {
            return null;
        }

        if (count($affectedContexts) > 1) {
            return match ($stateKey) {
                'has_backup_attention' => $this->filteredTenantRegistryTarget([
                    'backup_posture' => [
                        TenantBackupHealthAssessment::POSTURE_ABSENT,
                        TenantBackupHealthAssessment::POSTURE_STALE,
                        TenantBackupHealthAssessment::POSTURE_DEGRADED,
                    ],
                    'triage_sort' => TenantRecoveryTriagePresentation::TRIAGE_SORT_WORST_FIRST,
                ], __('localization.shell.choose_environment')),
                'has_recovery_attention' => $this->filteredTenantRegistryTarget([
                    'recovery_evidence' => [
                        TenantRecoveryTriagePresentation::RECOVERY_EVIDENCE_WEAKENED,
                        TenantRecoveryTriagePresentation::RECOVERY_EVIDENCE_UNVALIDATED,
                    ],
                    'triage_sort' => TenantRecoveryTriagePresentation::TRIAGE_SORT_WORST_FIRST,
                ], __('localization.shell.choose_environment')),
                default => $this->chooseEnvironmentTarget(__('localization.shell.choose_environment')),
            };
        }

        $tenant = $affectedContexts[0]['tenant'] ?? null;

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

        return $this->tenantDashboardTarget(
            $tenant,
            $user,
            arrivalState: $this->workspaceOverviewArrivalStateFromContext($tenant, $affectedContexts[0]),
        );
    }

    /**
     * @param  array<int, int>  $accessibleTenantIds
     * @return list<array<string, mixed>>
     */
    private function recentOperations(
        int $workspaceId,
        array $accessibleTenantIds,
        CanonicalNavigationContext $navigationContext,
    ): array {
        return $this->scopeToAuthorizedTenants(
            OperationRun::query()->with('tenant'),
            $workspaceId,
            $accessibleTenantIds,
        )
            ->latest('created_at')
            ->limit(5)
            ->get()
            ->map(function (OperationRun $run) use ($navigationContext): array {
                $destination = $this->operationDetailTarget($run, $navigationContext);
                $statusSpec = BadgeRenderer::spec(BadgeDomain::OperationRunStatus, [
                    'status' => (string) $run->status,
                    'freshness_state' => $run->freshnessState()->value,
                ]);
                $outcomeSpec = BadgeRenderer::spec(BadgeDomain::OperationRunOutcome, [
                    'outcome' => (string) $run->outcome,
                    'status' => (string) $run->status,
                    'freshness_state' => $run->freshnessState()->value,
                ]);

                return [
                    'id' => (int) $run->getKey(),
                    'title' => OperationCatalog::label((string) $run->type),
                    'tenant_label' => $run->tenant instanceof ManagedEnvironment ? (string) $run->tenant->name : null,
                    'status_label' => $statusSpec->label,
                    'status_color' => $statusSpec->color,
                    'outcome_label' => $outcomeSpec->label,
                    'outcome_color' => $outcomeSpec->color,
                    'lifecycle_label' => OperationUxPresenter::lifecycleAttentionSummary($run),
                    'guidance' => OperationUxPresenter::surfaceGuidance($run),
                    'started_at' => $run->created_at?->diffForHumans() ?? 'just now',
                    'destination' => $destination,
                    'url' => $destination['url'],
                ];
            })
            ->all();
    }

    /**
     * @return array<string, mixed>
     */
    private function calmnessState(
        int $accessibleTenantCount,
        array $attentionItems,
        int $governanceAttentionTenantCount,
        int $backupAttentionTenantCount,
        int $recoveryAttentionTenantCount,
        int $totalProblemOperationsCount,
        int $totalActiveOperationsCount,
        int $totalAlertFailuresCount,
        bool $canViewAlerts,
        CanonicalNavigationContext $navigationContext,
    ): array {
        $checkedDomains = ['tenant_access', 'governance', 'findings', 'compare', 'backup_health', 'recovery_evidence', 'operations'];

        if ($canViewAlerts) {
            $checkedDomains[] = 'alerts';
        }

        if ($accessibleTenantCount === 0) {
            return [
                'is_calm' => false,
                'checked_domains' => $checkedDomains,
                'title' => 'No accessible environments in this workspace',
                'body' => 'This workspace is not calm or healthy yet because your current scope has no visible environments. Switch workspace or review workspace-wide operations while access is being restored.',
                'next_action' => $this->switchWorkspaceTarget(),
            ];
        }

        $hasPortfolioRecoveryAttention = $backupAttentionTenantCount > 0 || $recoveryAttentionTenantCount > 0;
        $hasActivityAttention = $totalActiveOperationsCount > 0
            || $totalProblemOperationsCount > 0
            || ($canViewAlerts && $totalAlertFailuresCount > 0);
        $isCalm = $governanceAttentionTenantCount === 0
            && ! $hasPortfolioRecoveryAttention
            && ! $hasActivityAttention;

        if ($isCalm) {
            return [
                'is_calm' => true,
                'checked_domains' => $checkedDomains,
                'title' => 'Nothing urgent in your visible workspace slice',
                'body' => 'Visible governance, backup health, recovery evidence, compare posture, and activity currently look calm. Backup health and recovery evidence are included in this visible workspace calmness check.',
                'next_action' => $this->chooseEnvironmentTarget(),
            ];
        }

        if ($attentionItems === []) {
            return [
                'is_calm' => false,
                'checked_domains' => $checkedDomains,
                'title' => 'Workspace activity still needs review',
                'body' => 'Backup health or recovery evidence still needs follow-up, or activity remains open in the visible workspace, but your current scope does not expose a more specific environment drill-through here. Review operations first.',
                'next_action' => $this->operationsIndexTarget(null, $navigationContext, 'active'),
            ];
        }

        return [
            'is_calm' => false,
            'checked_domains' => $checkedDomains,
            'title' => 'Visible environments still need attention',
            'body' => 'Backup health or recovery evidence still needs follow-up, or governance and activity remain open in this workspace.',
            'next_action' => $attentionItems[0]['destination'] ?? $this->chooseEnvironmentTarget(),
        ];
    }

    /**
     * @param  array<int, int>  $accessibleTenantIds
     */
    private function scopeToAuthorizedTenants(Builder $query, int $workspaceId, array $accessibleTenantIds): Builder
    {
        return $query
            ->where('workspace_id', $workspaceId)
            ->where(function (Builder $query) use ($accessibleTenantIds): void {
                $query->whereNull('managed_environment_id');

                if ($accessibleTenantIds !== []) {
                    $query->orWhereIn('managed_environment_id', $accessibleTenantIds);
                }
            });
    }

    /**
     * @param  array<int, int>  $accessibleTenantIds
     */
    private function scopeToVisibleTenants(Builder $query, int $workspaceId, array $accessibleTenantIds): Builder
    {
        return $query
            ->where('workspace_id', $workspaceId)
            ->whereIn('managed_environment_id', $accessibleTenantIds === [] ? [0] : $accessibleTenantIds);
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function quickActions(
        Workspace $workspace,
        int $accessibleTenantCount,
        bool $canViewAlerts,
        User $user,
        CanonicalNavigationContext $navigationContext,
    ): array {
        $actions = [
            [
                'key' => 'choose_environment',
                'label' => __('localization.shell.choose_environment'),
                'description' => 'Deliberately enter environment context from this workspace.',
                'url' => ChooseEnvironment::getUrl(panel: 'admin'),
                'icon' => 'heroicon-o-building-office-2',
                'color' => 'primary',
                'visible' => $accessibleTenantCount > 0,
            ],
            [
                'key' => 'operations',
                'label' => 'Open operations',
                'description' => 'Review current and recent workspace-wide operations.',
                'url' => OperationRunLinks::index(context: $navigationContext, allTenants: true),
                'icon' => 'heroicon-o-queue-list',
                'color' => 'gray',
                'visible' => true,
            ],
            [
                'key' => 'alerts',
                'label' => 'Open alerts',
                'description' => 'Inspect alert overview, rules, and deliveries.',
                'url' => $this->alertsOverviewUrl($navigationContext),
                'icon' => 'heroicon-o-bell-alert',
                'color' => 'gray',
                'visible' => $canViewAlerts,
            ],
            [
                'key' => 'switch_workspace',
                'label' => 'Switch workspace',
                'description' => 'Change the active workspace context.',
                'url' => $this->switchWorkspaceUrl(),
                'icon' => 'heroicon-o-arrows-right-left',
                'color' => 'gray',
                'visible' => true,
            ],
            [
                'key' => 'manage_workspaces',
                'label' => 'Manage workspaces',
                'description' => 'Open workspace management and memberships.',
                'url' => route('filament.admin.resources.workspaces.index'),
                'icon' => 'heroicon-o-squares-2x2',
                'color' => 'gray',
                'visible' => $this->canManageWorkspaces($workspace, $user),
            ],
        ];

        return collect($actions)
            ->filter(fn (array $action): bool => (bool) $action['visible'])
            ->map(function (array $action): array {
                unset($action['visible']);

                return $action;
            })
            ->values()
            ->all();
    }

    private function canManageWorkspaces(Workspace $workspace, User $user): bool
    {
        if ($this->workspaceCapabilityResolver->can($user, $workspace, Capabilities::WORKSPACE_MANAGE)) {
            return true;
        }

        $roles = WorkspaceRoleCapabilityMap::rolesWithCapability(Capabilities::WORKSPACE_MEMBERSHIP_MANAGE);
        $role = $this->workspaceCapabilityResolver->getRole($user, $workspace);

        return $role !== null && in_array($role->value, $roles, true);
    }

    private function tenantRouteKey(ManagedEnvironment $tenant): string
    {
        return filled($tenant->external_id)
            ? (string) $tenant->external_id
            : (string) $tenant->getKey();
    }

    /**
     * @return array<string, mixed>
     */
    private function chooseEnvironmentTarget(?string $label = null): array
    {
        return $this->destination(
            kind: 'choose_environment',
            url: ChooseEnvironment::getUrl(panel: 'admin'),
            label: $label ?? __('localization.shell.choose_environment'),
        );
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    private function filteredTenantRegistryTarget(array $filters, ?string $label = null): array
    {
        return $this->destination(
            kind: 'choose_environment',
            url: ManagedEnvironmentLinks::indexUrl(query: $filters),
            label: $label ?? __('localization.shell.choose_environment'),
            filters: $filters,
        );
    }

    /**
     * @return array<string, mixed>
     */
    private function switchWorkspaceTarget(string $label = 'Switch workspace'): array
    {
        return $this->destination(
            kind: 'switch_workspace',
            url: $this->switchWorkspaceUrl(),
            label: $label,
        );
    }

    /**
     * @return array<string, mixed>
     */
    private function tenantDashboardTarget(
        ManagedEnvironment $tenant,
        User $user,
        string $label = 'Open environment',
        ?array $arrivalState = null,
    ): array {
        if (! $this->canAccessEnvironmentDashboard($user, $tenant)) {
            return $this->disabledDestination(
                kind: 'tenant_dashboard',
                label: $label,
                tenant: $tenant,
            );
        }

        return $this->destination(
            kind: 'tenant_dashboard',
            url: $this->appendArrivalToken(
                ManagedEnvironmentLinks::viewUrl($tenant),
                $arrivalState,
            ),
            label: $label,
            tenant: $tenant,
        );
    }

    /**
     * @param  array{family: string, state: string|null, reason: string|null}  $reasonContext
     * @return array<string, mixed>|null
     */
    private function workspaceOverviewArrivalState(ManagedEnvironment $tenant, array $reasonContext): ?array
    {
        $family = is_string($reasonContext['family'] ?? null) ? $reasonContext['family'] : null;
        $state = is_string($reasonContext['state'] ?? null) ? $reasonContext['state'] : null;

        if ($family === null || $state === null) {
            return null;
        }

        return [
            'sourceSurface' => PortfolioArrivalContextToken::SOURCE_WORKSPACE_OVERVIEW,
            'tenantRouteKey' => $this->tenantRouteKey($tenant),
            'workspaceId' => (int) $tenant->workspace_id,
            'concernFamily' => $family,
            'concernState' => $state,
            'concernReason' => is_string($reasonContext['reason'] ?? null) ? $reasonContext['reason'] : null,
        ];
    }

    /**
     * @param  array<string, mixed>  $context
     * @return array<string, mixed>|null
     */
    private function workspaceOverviewArrivalStateFromContext(ManagedEnvironment $tenant, array $context): ?array
    {
        if (($context['has_backup_attention'] ?? false) === true) {
            return $this->workspaceOverviewArrivalState($tenant, [
                'family' => 'backup_health',
                'state' => is_string($context['backup_health_posture'] ?? null) ? $context['backup_health_posture'] : null,
                'reason' => is_string($context['backup_health_reason'] ?? null) ? $context['backup_health_reason'] : null,
            ]);
        }

        if (($context['has_recovery_attention'] ?? false) === true) {
            return $this->workspaceOverviewArrivalState($tenant, [
                'family' => 'recovery_evidence',
                'state' => is_string($context['recovery_evidence_state'] ?? null) ? $context['recovery_evidence_state'] : null,
                'reason' => is_string($context['recovery_evidence_reason'] ?? null) ? $context['recovery_evidence_reason'] : null,
            ]);
        }

        return null;
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    private function findingsTarget(ManagedEnvironment $tenant, User $user, array $filters, string $label = 'Open findings'): array
    {
        if ($this->canOpenFindings($user, $tenant)) {
            return $this->destination(
                kind: 'tenant_findings',
                url: FindingResource::getUrl('index', $filters, tenant: $tenant),
                label: $label,
                tenant: $tenant,
                filters: $filters,
            );
        }

        if ($this->canTenantView($user, $tenant)) {
            return $this->tenantDashboardTarget($tenant, $user, 'Open environment');
        }

        return $this->disabledDestination(
            kind: 'tenant_findings',
            label: $label,
            tenant: $tenant,
            filters: $filters,
        );
    }

    /**
     * @return array<string, mixed>
     */
    private function baselineCompareTarget(ManagedEnvironment $tenant, User $user, string $label = 'Open Baseline Compare'): array
    {
        if (! $this->canTenantView($user, $tenant)) {
            return $this->disabledDestination(
                kind: 'baseline_compare_landing',
                label: $label,
                tenant: $tenant,
            );
        }

        return $this->destination(
            kind: 'baseline_compare_landing',
            url: ManagedEnvironmentLinks::baselineCompareUrl($tenant),
            label: $label,
            tenant: $tenant,
        );
    }

    /**
     * @return array<string, mixed>
     */
    private function operationsIndexTarget(
        ?ManagedEnvironment $tenant,
        CanonicalNavigationContext $navigationContext,
        ?string $activeTab = null,
        ?string $problemClass = null,
        string $label = 'Open operations',
    ): array {
        return $this->destination(
            kind: 'operations_index',
            url: OperationRunLinks::index($tenant, $navigationContext, $activeTab, $tenant === null, $problemClass),
            label: $label,
            tenant: $tenant,
            filters: array_filter([
                'managed_environment_id' => $tenant?->getKey(),
                'activeTab' => $activeTab,
                'problemClass' => $problemClass,
            ], static fn (mixed $value): bool => $value !== null && $value !== ''),
        );
    }

    /**
     * @return array<string, mixed>
     */
    private function operationDetailTarget(OperationRun $run, CanonicalNavigationContext $navigationContext): array
    {
        $tenant = $run->tenant instanceof ManagedEnvironment ? $run->tenant : null;

        return $this->destination(
            kind: 'operation_detail',
            url: OperationRunLinks::tenantlessView($run, $navigationContext),
            label: 'Open operation',
            tenant: $tenant,
            filters: ['run' => (int) $run->getKey()],
        );
    }

    /**
     * @return array<string, mixed>
     */
    private function alertsOverviewTarget(CanonicalNavigationContext $navigationContext, bool $enabled, string $label = 'Open alerts'): array
    {
        if (! $enabled) {
            return $this->disabledDestination(
                kind: 'alerts_overview',
                label: $label,
            );
        }

        return $this->destination(
            kind: 'alerts_overview',
            url: $this->alertsOverviewUrl($navigationContext),
            label: $label,
        );
    }

    private function alertsOverviewUrl(CanonicalNavigationContext $navigationContext): string
    {
        return $this->appendQuery(route('filament.admin.alerts'), $navigationContext->toQuery());
    }

    private function canTenantView(User $user, ManagedEnvironment $tenant): bool
    {
        return $this->capabilityResolver->can($user, $tenant, Capabilities::TENANT_VIEW);
    }

    private function canAccessEnvironmentDashboard(User $user, ManagedEnvironment $tenant): bool
    {
        return $this->capabilityResolver->isMember($user, $tenant);
    }

    private function canOpenFindings(User $user, ManagedEnvironment $tenant): bool
    {
        return $this->capabilityResolver->can($user, $tenant, Capabilities::TENANT_FINDINGS_VIEW);
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    private function destination(
        string $kind,
        string $url,
        string $label,
        ?ManagedEnvironment $tenant = null,
        array $filters = [],
    ): array {
        return [
            'kind' => $kind,
            'url' => $url,
            'tenant_route_key' => $tenant instanceof ManagedEnvironment ? $this->tenantRouteKey($tenant) : null,
            'label' => $label,
            'disabled' => false,
            'helper_text' => null,
            'filters' => $filters !== [] ? $filters : null,
        ];
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    private function disabledDestination(
        string $kind,
        string $label,
        ?ManagedEnvironment $tenant = null,
        array $filters = [],
    ): array {
        return [
            'kind' => $kind,
            'url' => null,
            'tenant_route_key' => $tenant instanceof ManagedEnvironment ? $this->tenantRouteKey($tenant) : null,
            'label' => $label,
            'disabled' => true,
            'helper_text' => UiTooltips::INSUFFICIENT_PERMISSION,
            'filters' => $filters !== [] ? $filters : null,
        ];
    }

    private function workspaceOverviewNavigationContext(): CanonicalNavigationContext
    {
        return new CanonicalNavigationContext(
            sourceSurface: 'workspace.overview',
            canonicalRouteName: 'admin.home',
            tenantId: null,
            backLinkLabel: 'Back to overview',
            backLinkUrl: route('admin.home'),
        );
    }

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

    /**
     * @param  array<string, mixed>|null  $arrivalState
     */
    private function appendArrivalToken(string $url, ?array $arrivalState = null): string
    {
        if ($arrivalState === null) {
            return $url;
        }

        return $this->appendQuery($url, [
            PortfolioArrivalContextToken::QUERY_PARAMETER => PortfolioArrivalContextToken::encode($arrivalState),
        ]);
    }

    private function switchWorkspaceUrl(): string
    {
        return route('filament.admin.pages.choose-workspace').'?choose=1';
    }
}
