<?php

declare(strict_types=1);

namespace App\Filament\Widgets\ManagedEnvironment;

use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\ReviewPack;
use App\Models\User;
use App\Services\ReviewPackService;
use App\Support\Auth\Capabilities;
use App\Support\OperationRunLinks;
use App\Support\OperationRunType;
use App\Support\OpsUx\OperationUxPresenter;
use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\ReasonTranslation\ReasonPresenter;
use App\Support\ReviewPackStatus;
use Filament\Actions\Action;
use Filament\Facades\Filament;
use Filament\Notifications\Notification;
use Filament\Widgets\Widget;

class ManagedEnvironmentReviewPackCard extends Widget
{
    private const string ACTIVE_POLLING_INTERVAL = '10s';

    protected static bool $isLazy = false;

    protected string $view = 'filament.widgets.managed-environment.managed-environment-review-pack-card';

    public ?ManagedEnvironment $record = null;

    private function resolveTenant(): ?ManagedEnvironment
    {
        $tenant = Filament::getTenant();

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

        return $this->record instanceof ManagedEnvironment ? $this->record : null;
    }

    public function generatePack(bool $includePii = true, bool $includeOperations = true): void
    {
        $user = auth()->user();

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

        $tenant = $this->resolveTenant();

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

        if (! $user->canAccessTenant($tenant)) {
            abort(404);
        }

        if (! $user->can(Capabilities::REVIEW_PACK_MANAGE, $tenant)) {
            abort(403);
        }

        /** @var ReviewPackService $service */
        $service = app(ReviewPackService::class);

        $decision = $service->reviewPackGenerationDecisionForTenant($tenant);

        if ((bool) ($decision['is_blocked'] ?? false)) {
            Notification::make()
                ->title('Review pack generation unavailable')
                ->body((string) ($decision['block_reason'] ?? 'Workspace entitlement currently blocks review pack generation.'))
                ->warning()
                ->send();

            return;
        }

        if ((bool) ($decision['is_warning'] ?? false) && is_string($decision['warning_reason'] ?? null)) {
            Notification::make()
                ->title('Review pack generation allowed with warning')
                ->body($decision['warning_reason'])
                ->warning()
                ->send();
        }

        $activeRun = $service->checkActiveRun($tenant)
            ? OperationRun::query()
                ->where('managed_environment_id', (int) $tenant->getKey())
                ->where('type', OperationRunType::ReviewPackGenerate->value)
                ->active()
                ->orderByDesc('id')
                ->first()
            : null;

        if ($activeRun) {
            OpsUxBrowserEvents::dispatchRunEnqueued($this);

            OperationUxPresenter::alreadyQueuedToast((string) $activeRun->type)
                ->body('A review pack is already queued or running for this tenant.')
                ->actions([
                    Action::make('view_run')
                        ->label('Open operation')
                        ->url(OperationRunLinks::tenantlessView($activeRun)),
                ])
                ->send();

            return;
        }

        try {
            $reviewPack = $service->generate($tenant, $user, [
                'include_pii' => $includePii,
                'include_operations' => $includeOperations,
            ]);
        } catch (WorkspaceEntitlementBlockedException $exception) {
            Notification::make()
                ->title('Review pack generation unavailable')
                ->body($exception->getMessage())
                ->warning()
                ->send();

            return;
        }

        $runUrl = $reviewPack->operationRun
            ? OperationRunLinks::tenantlessView($reviewPack->operationRun)
            : null;

        OpsUxBrowserEvents::dispatchRunEnqueued($this);

        $toast = OperationUxPresenter::queuedToast(OperationRunType::ReviewPackGenerate->value)
            ->body('The pack will be generated in the background. You will be notified when it is ready.');

        if ($runUrl !== null) {
            $toast->actions([
                Action::make('view_run')
                    ->label('Open operation')
                    ->url($runUrl),
            ]);
        }

        $toast->send();
    }

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

        if (! $tenant instanceof ManagedEnvironment) {
            return $this->emptyState();
        }

        $user = auth()->user();
        $isTenantMember = $user instanceof User && $user->canAccessTenant($tenant);
        $canView = $isTenantMember && $user->can(Capabilities::REVIEW_PACK_VIEW, $tenant);
        $canManage = $isTenantMember && $user->can(Capabilities::REVIEW_PACK_MANAGE, $tenant);
        $service = app(ReviewPackService::class);
        $generationEntitlement = $canManage
            ? $service->reviewPackGenerationDecisionForTenant($tenant)
            : null;
        $generationBlocked = (bool) ($generationEntitlement['is_blocked'] ?? false);
        $generationBlockReason = is_string($generationEntitlement['block_reason'] ?? null)
            ? $generationEntitlement['block_reason']
            : null;
        $generationWarningReason = is_string($generationEntitlement['warning_reason'] ?? null)
            ? $generationEntitlement['warning_reason']
            : null;

        $latestPack = ReviewPack::query()
            ->with(['environmentReview', 'operationRun'])
            ->where('managed_environment_id', (int) $tenant->getKey())
            ->orderByDesc('created_at')
            ->orderByDesc('id')
            ->first();

        if (! $latestPack instanceof ReviewPack) {
            return [
                'tenant' => $tenant,
                'pack' => null,
                'statusEnum' => null,
                'pollingInterval' => null,
                'canView' => $canView,
                'canManage' => $canManage,
                'generationBlocked' => $generationBlocked,
                'generationBlockReason' => $generationBlockReason,
                'generationWarningReason' => $generationWarningReason,
                'customerWorkspaceUrl' => $canView ? CustomerReviewWorkspace::environmentFilterUrl($tenant) : null,
                'downloadUrl' => null,
                'failedReason' => null,
                'reviewUrl' => null,
            ];
        }

        $statusEnum = ReviewPackStatus::tryFrom((string) $latestPack->status);

        $downloadUrl = null;
        if ($statusEnum === ReviewPackStatus::Ready && $canView) {
            /** @var ReviewPackService $service */
            $service = app(ReviewPackService::class);
            $downloadUrl = $service->generateDownloadUrl($latestPack);
        }

        $reviewUrl = null;
        if ($latestPack->environmentReview && $canView) {
            $reviewUrl = \App\Filament\Resources\EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $latestPack->environmentReview], $tenant);
        }

        $failedReason = null;
        $failedReasonDetail = null;
        $failedReasonSemantics = null;

        if ($statusEnum === ReviewPackStatus::Failed && $latestPack->operationRun) {
            $reasonPresenter = app(ReasonPresenter::class);
            $failedEnvelope = $reasonPresenter->forOperationRun($latestPack->operationRun, 'review_pack_widget');

            if ($failedEnvelope !== null) {
                $failedReason = $failedEnvelope->operatorLabel;
                $failedReasonDetail = $failedEnvelope->shortExplanation;
                $failedReasonSemantics = $reasonPresenter->semantics($failedEnvelope);
            }

            $opContext = is_array($latestPack->operationRun->context) ? $latestPack->operationRun->context : [];

            if ($failedReason === null) {
                $failedReason = (string) ($opContext['reason_code'] ?? 'Unknown error');
            }
        }

        return [
            'tenant' => $tenant,
            'pack' => $latestPack,
            'statusEnum' => $statusEnum,
            'pollingInterval' => self::resolvePollingInterval($latestPack),
            'canView' => $canView,
            'canManage' => $canManage,
            'generationBlocked' => $generationBlocked,
            'generationBlockReason' => $generationBlockReason,
            'generationWarningReason' => $generationWarningReason,
            'customerWorkspaceUrl' => $canView ? CustomerReviewWorkspace::environmentFilterUrl($tenant) : null,
            'downloadUrl' => $downloadUrl,
            'failedReason' => $failedReason,
            'failedReasonDetail' => $failedReasonDetail,
            'failedReasonSemantics' => $failedReasonSemantics,
            'reviewUrl' => $reviewUrl,
        ];
    }

    public static function resolvePollingInterval(?ReviewPack $pack): ?string
    {
        $status = ReviewPackStatus::tryFrom((string) $pack?->status);

        return match ($status) {
            ReviewPackStatus::Queued, ReviewPackStatus::Generating => self::ACTIVE_POLLING_INTERVAL,
            default => null,
        };
    }

    /**
     * @return array<string, mixed>
     */
    private function emptyState(): array
    {
        return [
            'tenant' => null,
            'pack' => null,
            'statusEnum' => null,
            'pollingInterval' => null,
            'canView' => false,
            'canManage' => false,
            'generationBlocked' => false,
            'generationBlockReason' => null,
            'generationWarningReason' => null,
            'customerWorkspaceUrl' => null,
            'downloadUrl' => null,
            'failedReason' => null,
            'failedReasonDetail' => null,
            'failedReasonSemantics' => null,
            'reviewUrl' => null,
        ];
    }
}
