<?php

declare(strict_types=1);

namespace App\Filament\Widgets\Dashboard;

use App\Filament\Resources\BackupScheduleResource;
use App\Filament\Resources\BackupSetResource;
use App\Filament\Resources\RestoreRunResource;
use App\Models\RestoreRun;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Support\Auth\Capabilities;
use App\Support\BackupHealth\BackupHealthActionTarget;
use App\Support\BackupHealth\TenantBackupHealthAssessment;
use App\Support\BackupHealth\TenantBackupHealthResolver;
use App\Support\OpsUx\ActiveRuns;
use App\Support\Rbac\UiTooltips;
use App\Support\RestoreSafety\RestoreResultAttention;
use App\Support\RestoreSafety\RestoreSafetyResolver;
use App\Support\Tenants\TenantRecoveryTriagePresentation;
use Filament\Facades\Filament;
use Filament\Widgets\Widget;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class RecoveryReadiness extends Widget
{
    protected int|string|array $columnSpan = 'full';

    protected string $view = 'filament.widgets.dashboard.recovery-readiness';

    protected function getPollingInterval(): ?string
    {
        return ActiveRuns::pollingIntervalForTenant(Filament::getTenant());
    }

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

        if (! $tenant instanceof ManagedEnvironment) {
            return [
                'pollingInterval' => null,
                'backupPosture' => $this->emptyStatPayload('Backup posture'),
                'recoveryEvidence' => $this->emptyStatPayload('Recovery evidence'),
            ];
        }

        $backupHealth = $this->backupHealthAssessment($tenant);
        $backupHealthAction = $this->resolveBackupHealthAction($tenant, $backupHealth->primaryActionTarget);
        $recoveryEvidence = $this->recoveryEvidence($tenant);
        $recoveryAction = $this->resolveRecoveryAction($tenant, $recoveryEvidence);

        return [
            'pollingInterval' => ActiveRuns::pollingIntervalForTenant($tenant),
            'backupPosture' => [
                'label' => 'Backup posture',
                'value' => TenantRecoveryTriagePresentation::backupPostureLabel($backupHealth),
                'description' => TenantRecoveryTriagePresentation::backupPostureDescription($backupHealth, $backupHealthAction['helperText']),
                'color' => TenantRecoveryTriagePresentation::backupPostureTone($backupHealth),
                'url' => $backupHealthAction['actionUrl'],
            ],
            'recoveryEvidence' => [
                'label' => 'Recovery evidence',
                'value' => TenantRecoveryTriagePresentation::recoveryEvidenceLabel($recoveryEvidence),
                'description' => TenantRecoveryTriagePresentation::recoveryEvidenceDescription($recoveryEvidence, $recoveryAction['helperText']),
                'color' => TenantRecoveryTriagePresentation::recoveryEvidenceTone($recoveryEvidence),
                'url' => $recoveryAction['actionUrl'],
            ],
        ];
    }

    /**
     * @return array{label: string, value: string, description: string|null, color: string, url: string|null}
     */
    private function emptyStatPayload(string $label): array
    {
        return [
            'label' => $label,
            'value' => '—',
            'description' => null,
            'color' => 'gray',
            'url' => null,
        ];
    }

    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);
    }

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

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

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

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

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

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

    /**
     * @param  array<string, mixed>  $recoveryEvidence
     * @return array{actionUrl: string|null, helperText: string|null}
     */
    private function resolveRecoveryAction(ManagedEnvironment $tenant, array $recoveryEvidence): array
    {
        if (! $this->canOpenRestoreHistory($tenant)) {
            return ['actionUrl' => null, '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 [
                'actionUrl' => $this->restoreRunListUrl($tenant, $reason),
                'helperText' => null,
            ];
        }

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

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

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

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

    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);
    }
}
