<?php

declare(strict_types=1);

namespace App\Filament\Widgets\ManagedEnvironment;

use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentTriageReview;
use App\Models\User;
use App\Services\PortfolioTriage\ManagedEnvironmentTriageReviewService;
use App\Support\Auth\Capabilities;
use App\Support\BackupHealth\TenantBackupHealthResolver;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\PortfolioTriage\PortfolioArrivalContext;
use App\Support\PortfolioTriage\PortfolioArrivalContextResolver;
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewStateResolver;
use App\Support\Rbac\UiEnforcement;
use App\Support\RestoreSafety\RestoreSafetyResolver;
use Filament\Actions\Action;
use Filament\Actions\Concerns\InteractsWithActions;
use Filament\Actions\Contracts\HasActions;
use Filament\Facades\Filament;
use Filament\Notifications\Notification;
use Filament\Schemas\Concerns\InteractsWithSchemas;
use Filament\Schemas\Contracts\HasSchemas;
use Filament\Widgets\Widget;

class ManagedEnvironmentTriageArrivalContinuity extends Widget implements HasActions, HasSchemas
{
    use InteractsWithActions;
    use InteractsWithSchemas;

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

    private ?PortfolioArrivalContext $cachedArrivalContext = null;

    private ?int $cachedArrivalContextTenantId = null;

    private bool $hasCachedArrivalContext = false;

    /**
     * @var array{backupHealth: \App\Support\BackupHealth\TenantBackupHealthAssessment, recoveryEvidence: array<string, mixed>}|null
     */
    private ?array $cachedConcernTruth = null;

    private ?int $cachedConcernTruthTenantId = null;

    /**
     * @var array<int, array<string, array<string, mixed>|null>>
     */
    private array $cachedReviewStates = [];

    protected static bool $isLazy = false;

    protected int|string|array $columnSpan = 'full';

    protected string $view = 'filament.widgets.managed-environment.triage-arrival-continuity';

    public function mount(): void
    {
        $this->arrivalState = PortfolioArrivalContextToken::decode(
            request()->query(PortfolioArrivalContextToken::QUERY_PARAMETER),
        );
    }

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

        if (! $tenant instanceof ManagedEnvironment) {
            return ['context' => null, 'reviewState' => null];
        }

        $context = $this->resolveArrivalContext($tenant);

        if ($context === null) {
            return ['context' => null, 'reviewState' => null];
        }

        return [
            'context' => $context,
            'reviewState' => $this->currentReviewStateFor($tenant, $context->concernFamily),
        ];
    }

    public function markReviewedAction(): Action
    {
        return UiEnforcement::forAction(
            Action::make('markReviewed')
                ->label('Mark reviewed')
                ->icon('heroicon-o-check-circle')
                ->color('success')
                ->requiresConfirmation()
                ->modalHeading('Mark reviewed')
                ->modalDescription($this->reviewModalDescription(ManagedEnvironmentTriageReview::STATE_REVIEWED))
                ->visible(fn (): bool => $this->canShowReviewActions())
                ->action(function (ManagedEnvironmentTriageReviewService $service): void {
                    $this->handleReviewMutation(ManagedEnvironmentTriageReview::STATE_REVIEWED, $service);
                }),
        )
            ->preserveVisibility()
            ->requireCapability(Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE)
            ->apply();
    }

    public function markFollowUpNeededAction(): Action
    {
        return UiEnforcement::forAction(
            Action::make('markFollowUpNeeded')
                ->label('Mark follow-up needed')
                ->icon('heroicon-o-exclamation-triangle')
                ->color('warning')
                ->requiresConfirmation()
                ->modalHeading('Mark follow-up needed')
                ->modalDescription($this->reviewModalDescription(ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED))
                ->visible(fn (): bool => $this->canShowReviewActions())
                ->action(function (ManagedEnvironmentTriageReviewService $service): void {
                    $this->handleReviewMutation(ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED, $service);
                }),
        )
            ->preserveVisibility()
            ->requireCapability(Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE)
            ->apply();
    }

    private function canShowReviewActions(): bool
    {
        $tenant = Filament::getTenant();

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

        $context = $this->resolveArrivalContext($tenant);

        if ($context === null) {
            return false;
        }

        return ($this->currentReviewStateFor($tenant, $context->concernFamily)['current_concern_present'] ?? false) === true;
    }

    private function reviewModalDescription(string $targetManualState): \Closure
    {
        return function () use ($targetManualState): string {
            $tenant = Filament::getTenant();

            if (! $tenant instanceof ManagedEnvironment) {
                return 'This triage session is no longer available.';
            }

            $context = $this->resolveArrivalContext($tenant);

            if ($context === null) {
                return 'This triage session is no longer available.';
            }

            $reviewState = $this->currentReviewStateFor($tenant, $context->concernFamily);

            if (($reviewState['current_concern_present'] ?? false) !== true) {
                return 'This triage session no longer points at a current concern.';
            }

            $currentLabel = BadgeRenderer::spec(
                BadgeDomain::ManagedEnvironmentTriageReviewState,
                (string) ($reviewState['derived_state'] ?? ManagedEnvironmentTriageReview::DERIVED_STATE_NOT_REVIEWED),
            )->label;
            $targetLabel = BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $targetManualState)->label;

            return implode("\n\n", [
                'Concern family: '.$this->concernFamilyLabel($context->concernFamily),
                'Current review state: '.$currentLabel,
                'Target state: '.$targetLabel,
                'Scope: TenantPilot only. This updates shared triage progress and does not change backup posture or recovery evidence.',
            ]);
        };
    }

    private function handleReviewMutation(string $targetManualState, ManagedEnvironmentTriageReviewService $service): void
    {
        $tenant = Filament::getTenant();

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

        $context = $this->resolveArrivalContext($tenant);

        if ($context === null) {
            Notification::make()
                ->title('No triage session available')
                ->warning()
                ->send();

            return;
        }

        $reviewState = $this->currentReviewStateFor($tenant, $context->concernFamily);

        if (($reviewState['current_concern_present'] ?? false) !== true) {
            Notification::make()
                ->title('No current concern to update')
                ->body('This arrival context no longer maps to an active concern.')
                ->warning()
                ->send();

            return;
        }

        $concernTruth = $this->concernTruthFor($tenant);
        $actor = auth()->user();

        $review = match ($targetManualState) {
            ManagedEnvironmentTriageReview::STATE_REVIEWED => $service->markReviewed(
                tenant: $tenant,
                concernFamily: $context->concernFamily,
                backupHealth: $concernTruth['backupHealth'],
                recoveryEvidence: $concernTruth['recoveryEvidence'],
                actor: $actor instanceof User ? $actor : null,
            ),
            ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED => $service->markFollowUpNeeded(
                tenant: $tenant,
                concernFamily: $context->concernFamily,
                backupHealth: $concernTruth['backupHealth'],
                recoveryEvidence: $concernTruth['recoveryEvidence'],
                actor: $actor instanceof User ? $actor : null,
            ),
            default => null,
        };

        if (! $review instanceof ManagedEnvironmentTriageReview) {
            return;
        }

        $this->clearConcernCachesFor($tenant);

        Notification::make()
            ->title('Review state updated')
            ->body(sprintf(
                '%s is now %s for %s.',
                $tenant->name,
                BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $review->current_state)->label,
                $this->concernFamilyLabel($context->concernFamily),
            ))
            ->success()
            ->send();
    }

    /**
     * @return array<string, mixed>|null
     */
    private function currentReviewStateFor(ManagedEnvironment $tenant, string $concernFamily): ?array
    {
        $tenantId = (int) $tenant->getKey();

        if (array_key_exists($tenantId, $this->cachedReviewStates)
            && array_key_exists($concernFamily, $this->cachedReviewStates[$tenantId])) {
            return $this->cachedReviewStates[$tenantId][$concernFamily];
        }

        $concernTruth = $this->concernTruthFor($tenant);

        $reviewState = app(ManagedEnvironmentTriageReviewStateResolver::class)->resolveMany(
            workspaceId: (int) $tenant->workspace_id,
            tenantIds: [$tenantId],
            backupHealthByTenant: [$tenantId => $concernTruth['backupHealth']],
            recoveryEvidenceByTenant: [$tenantId => $concernTruth['recoveryEvidence']],
        )['rows'][$tenantId][$concernFamily] ?? null;

        $this->cachedReviewStates[$tenantId][$concernFamily] = $reviewState;

        return $reviewState;
    }

    /**
     * @return array{backupHealth: \App\Support\BackupHealth\TenantBackupHealthAssessment, recoveryEvidence: array<string, mixed>}
     */
    private function concernTruthFor(ManagedEnvironment $tenant): array
    {
        $tenantId = (int) $tenant->getKey();

        if ($this->cachedConcernTruthTenantId === $tenantId && is_array($this->cachedConcernTruth)) {
            return $this->cachedConcernTruth;
        }

        $this->cachedConcernTruthTenantId = $tenantId;
        $this->cachedConcernTruth = [
            'backupHealth' => app(TenantBackupHealthResolver::class)->assess($tenant),
            'recoveryEvidence' => app(RestoreSafetyResolver::class)->dashboardRecoveryEvidence($tenant),
        ];

        return $this->cachedConcernTruth;
    }

    private function clearConcernCachesFor(ManagedEnvironment $tenant): void
    {
        $tenantId = (int) $tenant->getKey();

        if ($this->cachedConcernTruthTenantId === $tenantId) {
            $this->cachedConcernTruthTenantId = null;
            $this->cachedConcernTruth = null;
        }

        if (array_key_exists($tenantId, $this->cachedReviewStates)) {
            unset($this->cachedReviewStates[$tenantId]);
        }
    }

    private function concernFamilyLabel(string $concernFamily): string
    {
        return match ($concernFamily) {
            PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH => 'Backup health',
            PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE => 'Recovery evidence',
            default => 'Portfolio concern',
        };
    }

    private function resolveArrivalContext(ManagedEnvironment $tenant): ?PortfolioArrivalContext
    {
        $tenantId = (int) $tenant->getKey();

        if ($this->arrivalState === null) {
            $this->cachedArrivalContextTenantId = $tenantId;
            $this->cachedArrivalContext = null;
            $this->hasCachedArrivalContext = true;

            return null;
        }

        if ($this->hasCachedArrivalContext && $this->cachedArrivalContextTenantId === $tenantId) {
            return $this->cachedArrivalContext;
        }

        $concernTruth = $this->concernTruthFor($tenant);

        $this->cachedArrivalContextTenantId = $tenantId;
        $this->cachedArrivalContext = app(PortfolioArrivalContextResolver::class)->resolveStateWithTruth(
            $tenant,
            $this->arrivalState,
            $concernTruth['backupHealth'],
            $concernTruth['recoveryEvidence'],
        );
        $this->hasCachedArrivalContext = true;

        return $this->cachedArrivalContext;
    }
}
