<?php

declare(strict_types=1);

namespace App\Filament\Widgets\Dashboard;

use App\Filament\Resources\BackupScheduleResource;
use App\Filament\Resources\BackupSetResource;
use App\Filament\Resources\FindingResource;
use App\Filament\Resources\RestoreRunResource;
use App\Models\FindingException;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\RestoreRun;
use App\Models\User;
use App\Support\Auth\Capabilities;
use App\Support\BackupHealth\BackupHealthActionTarget;
use App\Support\BackupHealth\BackupHealthDashboardSignal;
use App\Support\BackupHealth\TenantBackupHealthAssessment;
use App\Support\BackupHealth\TenantBackupHealthResolver;
use App\Support\Baselines\TenantGovernanceAggregate;
use App\Support\Baselines\TenantGovernanceAggregateResolver;
use App\Support\ManagedEnvironmentLinks;
use App\Support\OperationRunLinks;
use App\Support\OpsUx\ActiveRuns;
use App\Support\Rbac\UiTooltips;
use App\Support\RestoreSafety\RestoreResultAttention;
use App\Support\RestoreSafety\RestoreSafetyCopy;
use App\Support\RestoreSafety\RestoreSafetyResolver;
use Filament\Facades\Filament;
use Filament\Widgets\Widget;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class NeedsAttention extends Widget
{
    protected string $view = 'filament.widgets.dashboard.needs-attention';

    /**
     * @return array<string, mixed>
     */
    protected function getViewData(): array
    {
        $tenant = Filament::getTenant();

        if (! $tenant instanceof ManagedEnvironment) {
            return [
                'pollingInterval' => null,
                'items' => [],
                'healthyChecks' => [],
            ];
        }

        $tenantId = (int) $tenant->getKey();
        $aggregate = $this->governanceAggregate($tenant);
        $compareAssessment = $aggregate->summaryAssessment;
        $backupHealth = $this->backupHealthAssessment($tenant);
        $recoveryEvidence = $this->recoveryEvidence($tenant);

        $items = [];

        if (($recoveryItem = $this->recoveryEvidenceAttentionItem($tenant, $backupHealth, $recoveryEvidence)) !== null) {
            $items[] = $recoveryItem;
        }

        if (($backupHealthItem = $this->backupHealthAttentionItem($tenant, $backupHealth)) instanceof BackupHealthDashboardSignal) {
            $items[] = array_merge(
                $backupHealthItem->toArray(),
                $this->backupHealthActionPayload($tenant, $backupHealthItem->actionTarget, $backupHealthItem->actionLabel)
            );
        }

        $overdueOpenCount = $aggregate->overdueOpenFindingsCount;
        $lapsedGovernanceCount = $aggregate->lapsedGovernanceCount;
        $expiringGovernanceCount = $aggregate->expiringGovernanceCount;
        $highSeverityCount = $aggregate->highSeverityActiveFindingsCount;
        $staleActiveOperationsCount = (int) OperationRun::query()
            ->where('managed_environment_id', $tenantId)
            ->activeStaleAttention()
            ->count();
        $terminalFollowUpOperationsCount = (int) OperationRun::query()
            ->where('managed_environment_id', $tenantId)
            ->terminalFollowUp()
            ->count();
        $activeRuns = (int) OperationRun::query()
            ->where('managed_environment_id', $tenantId)
            ->healthyActive()
            ->count();

        if ($lapsedGovernanceCount > 0) {
            $items[] = [
                'key' => 'lapsed_governance',
                'title' => 'Lapsed accepted-risk governance',
                'body' => "{$lapsedGovernanceCount} accepted-risk finding(s) no longer have valid supporting governance.",
                'badge' => 'Governance',
                'badgeColor' => 'danger',
                ...$this->findingsAction(
                    $tenant,
                    'Open findings',
                    [
                        'tab' => 'risk_accepted',
                        'governance_validity' => FindingException::VALIDITY_MISSING_SUPPORT,
                    ],
                ),
            ];
        }

        if ($overdueOpenCount > 0) {
            $items[] = [
                'key' => 'overdue_findings',
                'title' => 'Overdue findings',
                'body' => "{$overdueOpenCount} open finding(s) are overdue and still need workflow follow-up.",
                'badge' => 'Findings',
                'badgeColor' => 'danger',
                ...$this->findingsAction(
                    $tenant,
                    'Open findings',
                    ['tab' => 'overdue'],
                ),
            ];
        }

        if ($expiringGovernanceCount > 0) {
            $items[] = [
                'key' => 'expiring_governance',
                'title' => 'Expiring accepted-risk governance',
                'body' => "{$expiringGovernanceCount} accepted-risk finding(s) need governance review soon.",
                'badge' => 'Governance',
                'badgeColor' => 'warning',
                ...$this->findingsAction(
                    $tenant,
                    'Open findings',
                    [
                        'tab' => 'risk_accepted',
                        'governance_validity' => FindingException::VALIDITY_EXPIRING,
                    ],
                ),
            ];
        }

        if ($highSeverityCount > 0) {
            $items[] = [
                'key' => 'high_severity_active_findings',
                'title' => 'High severity active findings',
                'body' => "{$highSeverityCount} high or critical finding(s) are still active.",
                'badge' => 'Findings',
                'badgeColor' => 'danger',
                ...$this->findingsAction(
                    $tenant,
                    'Open findings',
                    [
                        'tab' => 'needs_action',
                        'high_severity' => 1,
                    ],
                ),
            ];
        }

        if ($compareAssessment->stateFamily !== 'positive') {
            $items[] = [
                'key' => 'baseline_compare_posture',
                'title' => 'Baseline compare posture',
                'body' => $compareAssessment->headline,
                'supportingMessage' => $compareAssessment->supportingMessage,
                'badge' => 'Baseline',
                'badgeColor' => $compareAssessment->tone,
                'actionLabel' => 'Open Baseline Compare',
                'actionUrl' => ManagedEnvironmentLinks::baselineCompareUrl($tenant),
            ];
        }

        if ($staleActiveOperationsCount > 0) {
            $items[] = [
                'key' => 'operations_stale_attention',
                'title' => 'Active operations look stale',
                'body' => "{$staleActiveOperationsCount} run(s) are still marked active but are past the lifecycle window.",
                'badge' => 'Operations',
                'badgeColor' => 'warning',
                'actionLabel' => 'Open stale operations',
                'actionUrl' => OperationRunLinks::index(
                    $tenant,
                    activeTab: OperationRun::PROBLEM_CLASS_ACTIVE_STALE_ATTENTION,
                    problemClass: OperationRun::PROBLEM_CLASS_ACTIVE_STALE_ATTENTION,
                ),
            ];
        }

        if ($terminalFollowUpOperationsCount > 0) {
            $items[] = [
                'key' => 'operations_terminal_follow_up',
                'title' => 'Terminal operations need follow-up',
                'body' => "{$terminalFollowUpOperationsCount} run(s) finished blocked, partially, failed, or were automatically reconciled.",
                'badge' => 'Operations',
                'badgeColor' => 'danger',
                'actionLabel' => 'Open terminal follow-up',
                'actionUrl' => OperationRunLinks::index(
                    $tenant,
                    activeTab: OperationRun::PROBLEM_CLASS_TERMINAL_FOLLOW_UP,
                    problemClass: OperationRun::PROBLEM_CLASS_TERMINAL_FOLLOW_UP,
                ),
            ];
        }

        $healthyChecks = [];

        if ($items === []) {
            $healthyChecks = [
                ...array_filter([$this->backupHealthHealthyCheck($backupHealth)]),
                ...array_filter([$this->recoveryEvidenceHealthyCheck($recoveryEvidence)]),
                [
                    'title' => 'Baseline compare looks trustworthy',
                    'body' => $aggregate->headline,
                ],
                [
                    'title' => 'No overdue findings',
                    'body' => 'No open findings are currently overdue for this tenant.',
                ],
                [
                    'title' => 'Accepted-risk governance is healthy',
                    'body' => 'No accepted-risk findings currently need governance follow-up.',
                ],
                [
                    'title' => 'No high severity active findings',
                    'body' => 'No high severity findings are currently open for this tenant.',
                ],
                $activeRuns > 0
                    ? [
                        'title' => 'Operations are active',
                        'body' => "{$activeRuns} run(s) are active, but nothing currently needs follow-up.",
                    ]
                    : [
                        'title' => 'No active operations',
                        'body' => 'Nothing is currently running for this tenant.',
                    ],
            ];
        }

        return [
            'pollingInterval' => ActiveRuns::pollingIntervalForTenant($tenant),
            'items' => $items,
            'healthyChecks' => $healthyChecks,
        ];
    }

    /**
     * @param  array<string, mixed>  $parameters
     * @return array<string, mixed>
     */
    private function findingsAction(ManagedEnvironment $tenant, string $label, array $parameters): array
    {
        $url = $this->canOpenFindings($tenant)
            ? FindingResource::getUrl('index', $parameters, tenant: $tenant)
            : null;

        return [
            'actionLabel' => $label,
            'actionUrl' => $url,
            'actionDisabled' => $url === null,
            'helperText' => $url === null ? UiTooltips::INSUFFICIENT_PERMISSION : null,
        ];
    }

    private function canOpenFindings(ManagedEnvironment $tenant): bool
    {
        $user = auth()->user();

        return $user instanceof User
            && $user->canAccessTenant($tenant)
            && $user->can(Capabilities::TENANT_FINDINGS_VIEW, $tenant);
    }

    private function governanceAggregate(ManagedEnvironment $tenant): TenantGovernanceAggregate
    {
        /** @var TenantGovernanceAggregateResolver $resolver */
        $resolver = app(TenantGovernanceAggregateResolver::class);

        /** @var TenantGovernanceAggregate $aggregate */
        $aggregate = $resolver->forTenant($tenant);

        return $aggregate;
    }

    private function backupHealthAssessment(ManagedEnvironment $tenant): TenantBackupHealthAssessment
    {
        /** @var TenantBackupHealthResolver $resolver */
        $resolver = app(TenantBackupHealthResolver::class);

        return $resolver->assess($tenant);
    }

    /**
     * @return array<string, mixed>
     */
    private function recoveryEvidence(ManagedEnvironment $tenant): array
    {
        /** @var RestoreSafetyResolver $resolver */
        $resolver = app(RestoreSafetyResolver::class);

        return $resolver->dashboardRecoveryEvidence($tenant);
    }

    private function backupHealthAttentionItem(ManagedEnvironment $tenant, TenantBackupHealthAssessment $assessment): ?BackupHealthDashboardSignal
    {
        if (! $assessment->hasActiveReason()) {
            return null;
        }

        return new BackupHealthDashboardSignal(
            title: $this->backupHealthAttentionTitle($assessment),
            body: $assessment->supportingMessage ?? $assessment->headline,
            tone: $assessment->tone(),
            badge: 'Backups',
            badgeColor: $assessment->tone(),
            actionTarget: $assessment->primaryActionTarget,
            actionLabel: $assessment->primaryActionTarget?->label,
        );
    }

    /**
     * @return array{title: string, body: string}|null
     */
    private function backupHealthHealthyCheck(TenantBackupHealthAssessment $assessment): ?array
    {
        if (! $assessment->healthyClaimAllowed) {
            return null;
        }

        return [
            'title' => 'Backups are recent and healthy',
            'body' => trim(implode(' ', array_filter([
                $assessment->supportingMessage ?? 'The latest completed backup is recent and shows no material degradation.',
                $assessment->positiveClaimBoundary,
            ], static fn (?string $part): bool => filled($part)))),
        ];
    }

    /**
     * @param  array<string, mixed>  $recoveryEvidence
     * @return array<string, mixed>|null
     */
    private function recoveryEvidenceAttentionItem(ManagedEnvironment $tenant, TenantBackupHealthAssessment $backupHealth, array $recoveryEvidence): ?array
    {
        $overviewState = is_string($recoveryEvidence['overview_state'] ?? null)
            ? $recoveryEvidence['overview_state']
            : null;

        if ($overviewState === 'unvalidated') {
            return [
                'key' => 'recovery_evidence_unvalidated',
                'title' => 'Recovery evidence is unvalidated',
                'body' => (string) ($recoveryEvidence['summary'] ?? 'No executed restore history is visible in the latest tenant restore records.'),
                'supportingMessage' => $backupHealth->positiveClaimBoundary,
                'badge' => 'Recovery',
                'badgeColor' => 'warning',
                'actionElevated' => true,
                ...$this->recoveryActionPayload($tenant, $recoveryEvidence, 'Open restore history'),
            ];
        }

        if ($overviewState !== 'weakened') {
            return null;
        }

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

        return [
            'key' => 'recovery_evidence_'.$attentionState,
            'title' => $this->recoveryAttentionTitle($attentionState),
            'body' => (string) ($recoveryEvidence['summary'] ?? 'Recent restore history weakens confidence.'),
            'supportingMessage' => trim(implode(' ', array_filter([
                $primaryNextAction,
                $claimBoundary,
            ], static fn (?string $part): bool => filled($part)))),
            'badge' => 'Recovery',
            'badgeColor' => $attentionState === RestoreResultAttention::STATE_FAILED ? 'danger' : 'warning',
            'actionElevated' => true,
            ...$this->recoveryActionPayload($tenant, $recoveryEvidence, 'Open restore run'),
        ];
    }

    /**
     * @param  array<string, mixed>  $recoveryEvidence
     * @return array{title: string, body: string}|null
     */
    private function recoveryEvidenceHealthyCheck(array $recoveryEvidence): ?array
    {
        if (($recoveryEvidence['overview_state'] ?? null) !== 'no_recent_issues_visible') {
            return null;
        }

        return [
            'title' => 'No recent restore issues visible',
            'body' => trim(implode(' ', array_filter([
                is_string($recoveryEvidence['summary'] ?? null) ? $recoveryEvidence['summary'] : null,
                is_string($recoveryEvidence['claim_boundary'] ?? null) ? $recoveryEvidence['claim_boundary'] : null,
            ], static fn (?string $part): bool => filled($part)))),
        ];
    }

    /**
     * @return array{actionLabel: string|null, actionUrl: string|null, actionDisabled: bool, helperText: string|null}
     */
    private function backupHealthActionPayload(ManagedEnvironment $tenant, ?BackupHealthActionTarget $target, ?string $label): array
    {
        if (! $target instanceof BackupHealthActionTarget) {
            return [
                'actionLabel' => $label,
                'actionUrl' => null,
                'actionDisabled' => false,
                'helperText' => null,
            ];
        }

        if (! $this->canOpenBackupSurfaces($tenant)) {
            return [
                'actionLabel' => $label ?? $target->label,
                'actionUrl' => null,
                'actionDisabled' => true,
                'helperText' => UiTooltips::INSUFFICIENT_PERMISSION,
            ];
        }

        return match ($target->surface) {
            BackupHealthActionTarget::SURFACE_BACKUP_SETS_INDEX => [
                'actionLabel' => $label ?? $target->label,
                'actionUrl' => BackupSetResource::getUrl('index', [
                    'backup_health_reason' => $target->reason,
                ], tenant: $tenant),
                'actionDisabled' => false,
                'helperText' => null,
            ],
            BackupHealthActionTarget::SURFACE_BACKUP_SCHEDULES_INDEX => [
                'actionLabel' => $label ?? $target->label,
                'actionUrl' => BackupScheduleResource::getUrl('index', [
                    'backup_health_reason' => $target->reason,
                ], tenant: $tenant),
                'actionDisabled' => false,
                'helperText' => null,
            ],
            BackupHealthActionTarget::SURFACE_BACKUP_SET_VIEW => $this->backupHealthBackupSetActionPayload($tenant, $target, $label),
            default => [
                'actionLabel' => $label,
                'actionUrl' => null,
                'actionDisabled' => false,
                'helperText' => null,
            ],
        };
    }

    /**
     * @return array{actionLabel: string|null, actionUrl: string|null, actionDisabled: bool, helperText: string|null}
     */
    private function backupHealthBackupSetActionPayload(ManagedEnvironment $tenant, BackupHealthActionTarget $target, ?string $label): array
    {
        if (! is_numeric($target->recordId)) {
            return [
                'actionLabel' => 'Open backup sets',
                'actionUrl' => BackupSetResource::getUrl('index', [
                    'backup_health_reason' => $target->reason,
                ], tenant: $tenant),
                'actionDisabled' => false,
                'helperText' => 'The latest backup detail is no longer available.',
            ];
        }

        try {
            BackupSetResource::resolveScopedRecordOrFail($target->recordId);

            return [
                'actionLabel' => $label ?? $target->label,
                'actionUrl' => BackupSetResource::getUrl('view', [
                    'record' => $target->recordId,
                    'backup_health_reason' => $target->reason,
                ], tenant: $tenant),
                'actionDisabled' => false,
                'helperText' => null,
            ];
        } catch (ModelNotFoundException) {
            return [
                'actionLabel' => 'Open backup sets',
                'actionUrl' => BackupSetResource::getUrl('index', [
                    'backup_health_reason' => $target->reason,
                ], tenant: $tenant),
                'actionDisabled' => false,
                'helperText' => 'The latest backup detail is no longer available.',
            ];
        }
    }

    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',
            TenantBackupHealthAssessment::REASON_SCHEDULE_FOLLOW_UP => 'Backup schedules need follow-up',
            default => $assessment->headline,
        };
    }

    private function canOpenBackupSurfaces(ManagedEnvironment $tenant): bool
    {
        $user = auth()->user();

        return $user instanceof User
            && $user->canAccessTenant($tenant)
            && $user->can(Capabilities::TENANT_VIEW, $tenant);
    }

    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',
        };
    }

    /**
     * @param  array<string, mixed>  $recoveryEvidence
     * @return array{actionLabel: string, actionUrl: string|null, actionDisabled: bool, helperText: string|null}
     */
    private function recoveryActionPayload(ManagedEnvironment $tenant, array $recoveryEvidence, string $label): array
    {
        if (! $this->canOpenRestoreHistory($tenant)) {
            return [
                'actionLabel' => $label,
                'actionUrl' => null,
                'actionDisabled' => true,
                'helperText' => UiTooltips::INSUFFICIENT_PERMISSION,
            ];
        }

        $reason = is_string($recoveryEvidence['reason'] ?? null) && $recoveryEvidence['reason'] !== ''
            ? $recoveryEvidence['reason']
            : 'no_history';
        $latestRun = $recoveryEvidence['latest_relevant_restore_run'] ?? null;

        if (! $latestRun instanceof RestoreRun) {
            return [
                'actionLabel' => $label,
                'actionUrl' => $this->restoreRunListUrl($tenant, $reason),
                'actionDisabled' => false,
                'helperText' => null,
            ];
        }

        try {
            RestoreRunResource::resolveScopedRecordOrFail($latestRun->getKey());

            return [
                'actionLabel' => $label,
                'actionUrl' => RestoreRunResource::getUrl('view', [
                    'record' => (int) $latestRun->getKey(),
                    'recovery_posture_reason' => $reason,
                ], tenant: $tenant),
                'actionDisabled' => false,
                'helperText' => null,
            ];
        } catch (ModelNotFoundException) {
            return [
                'actionLabel' => 'Open restore history',
                'actionUrl' => $this->restoreRunListUrl($tenant, $reason),
                'actionDisabled' => false,
                'helperText' => 'The latest restore detail is no longer available.',
            ];
        }
    }

    private function canOpenRestoreHistory(ManagedEnvironment $tenant): bool
    {
        $user = auth()->user();

        return $user instanceof User
            && $user->canAccessTenant($tenant)
            && $user->can(Capabilities::TENANT_VIEW, $tenant);
    }

    private function restoreRunListUrl(ManagedEnvironment $tenant, string $reason): string
    {
        return RestoreRunResource::getUrl('index', [
            'recovery_posture_reason' => $reason,
        ], tenant: $tenant);
    }
}
