<?php

declare(strict_types=1);

namespace App\Filament\Resources\BaselineProfileResource\Pages;

use App\Filament\Resources\BaselineProfileResource;
use App\Models\BaselineProfile;
use App\Models\BaselineTenantAssignment;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Auth\CapabilityResolver;
use App\Services\Baselines\BaselineCaptureService;
use App\Services\Baselines\BaselineCompareService;
use App\Support\Auth\Capabilities;
use App\Support\Baselines\BaselineCaptureMode;
use App\Support\Baselines\BaselineReasonCodes;
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\Rbac\WorkspaceUiEnforcement;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Filament\Actions\EditAction;
use Filament\Forms\Components\Select;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ViewRecord;

class ViewBaselineProfile extends ViewRecord
{
    protected static string $resource = BaselineProfileResource::class;

    protected function getHeaderActions(): array
    {
        return [
            $this->captureAction(),
            $this->compareNowAction(),
            ActionGroup::make([
                $this->compareAssignedTenantsAction(),
                EditAction::make()
                    ->visible(fn (): bool => $this->hasManageCapability()),
            ])
                ->label('More')
                ->icon('heroicon-m-ellipsis-vertical')
                ->color('gray'),
        ];
    }

    private function captureAction(): Action
    {
        /** @var BaselineProfile $profile */
        $profile = $this->getRecord();

        $captureMode = $profile->capture_mode instanceof BaselineCaptureMode
            ? $profile->capture_mode
            : BaselineCaptureMode::Opportunistic;

        $label = $captureMode === BaselineCaptureMode::FullContent
            ? 'Capture baseline (full content)'
            : 'Capture baseline';

        $modalDescription = $captureMode === BaselineCaptureMode::FullContent
            ? 'Select the source environment. This will capture content evidence on demand (redacted) and may take longer depending on scope.'
            : 'Select the source environment whose current inventory will be captured as the baseline snapshot.';

        $action = Action::make('capture')
            ->label($label)
            ->icon('heroicon-o-camera')
            ->color('primary')
            ->hidden(fn (): bool => $this->shouldHideCaptureAction())
            ->requiresConfirmation()
            ->modalHeading($label)
            ->modalDescription($modalDescription)
            ->form([
                Select::make('source_environment_id')
                    ->label('Source managed environment')
                    ->options(fn (): array => $this->getWorkspaceTenantOptions())
                    ->required()
                    ->searchable(),
            ])
            ->action(function (array $data): void {
                $user = auth()->user();

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

                /** @var BaselineProfile $profile */
                $profile = $this->getRecord();
                $sourceTenant = ManagedEnvironment::query()->find((int) $data['source_environment_id']);

                if (! $sourceTenant instanceof ManagedEnvironment) {
                    Notification::make()
                        ->title('Source environment not found')
                        ->danger()
                        ->send();

                    return;
                }

                $service = app(BaselineCaptureService::class);
                $result = $service->startCapture($profile, $sourceTenant, $user);

                if (! $result['ok']) {
                    $reasonCode = is_string($result['reason_code'] ?? null) ? (string) $result['reason_code'] : 'unknown';
                    $translation = app(ReasonPresenter::class)->forArtifactTruth($reasonCode, 'artifact_truth');
                    $message = is_string($translation?->shortExplanation) && trim($translation->shortExplanation) !== ''
                        ? trim($translation->shortExplanation)
                        : 'Reason: '.str_replace('.', ' ', $reasonCode);

                    Notification::make()
                        ->title('Cannot start capture')
                        ->body($message)
                        ->danger()
                        ->send();

                    return;
                }

                $run = $result['run'] ?? null;

                if (! $run instanceof \App\Models\OperationRun) {
                    Notification::make()
                        ->title('Cannot start capture')
                        ->body('Reason: missing operation run')
                        ->danger()
                        ->send();

                    return;
                }

                $viewAction = Action::make('view_run')
                    ->label('Open operation')
                    ->url(OperationRunLinks::view($run, $sourceTenant));

                if (! $run->wasRecentlyCreated && in_array((string) $run->status, ['queued', 'running'], true)) {
                    OpsUxBrowserEvents::dispatchRunEnqueued($this);

                    OperationUxPresenter::alreadyQueuedToast((string) $run->type)
                        ->actions([$viewAction])
                        ->send();

                    return;
                }

                OpsUxBrowserEvents::dispatchRunEnqueued($this);

                OperationUxPresenter::queuedToast((string) $run->type)
                    ->actions([$viewAction])
                    ->send();
            });

        return WorkspaceUiEnforcement::forTableAction($action, fn (): ?Workspace => Workspace::query()
            ->whereKey((int) $this->getRecord()->workspace_id)
            ->first())
            ->requireCapability(Capabilities::WORKSPACE_BASELINES_MANAGE)
            ->apply();
    }

    private function compareNowAction(): Action
    {
        /** @var BaselineProfile $profile */
        $profile = $this->getRecord();

        $captureMode = $profile->capture_mode instanceof BaselineCaptureMode
            ? $profile->capture_mode
            : BaselineCaptureMode::Opportunistic;

        $label = $captureMode === BaselineCaptureMode::FullContent
            ? 'Compare now (full content)'
            : 'Compare now';

        $modalDescription = $captureMode === BaselineCaptureMode::FullContent
            ? 'Select the target environment. This will refresh content evidence on demand (redacted) before comparing.'
            : 'Select the target environment to compare its current inventory against the effective current baseline snapshot.';

        return Action::make('compareNow')
            ->label($label)
            ->icon('heroicon-o-play')
            ->color('primary')
            ->hidden(fn (): bool => ! $this->profileHasConsumableSnapshot())
            ->requiresConfirmation()
            ->modalHeading($label)
            ->modalDescription($modalDescription)
            ->form([
                Select::make('target_environment_id')
                    ->label('Target managed environment')
                    ->options(fn (): array => $this->getEligibleCompareTenantOptions())
                    ->required()
                    ->searchable(),
            ])
            ->disabled(fn (): bool => $this->getEligibleCompareTenantOptions() === [] || ! $this->profileHasConsumableSnapshot())
            ->action(function (array $data): void {
                $user = auth()->user();

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

                /** @var BaselineProfile $profile */
                $profile = $this->getRecord();

                $targetTenant = ManagedEnvironment::query()->find((int) $data['target_environment_id']);

                if (! $targetTenant instanceof ManagedEnvironment || (int) $targetTenant->workspace_id !== (int) $profile->workspace_id) {
                    Notification::make()
                        ->title('Target environment not found')
                        ->danger()
                        ->send();

                    return;
                }

                $assignment = BaselineTenantAssignment::query()
                    ->where('workspace_id', (int) $profile->workspace_id)
                    ->where('managed_environment_id', (int) $targetTenant->getKey())
                    ->where('baseline_profile_id', (int) $profile->getKey())
                    ->first();

                if (! $assignment instanceof BaselineTenantAssignment) {
                    Notification::make()
                        ->title('ManagedEnvironment not assigned')
                        ->body('This tenant is not assigned to this baseline profile.')
                        ->warning()
                        ->send();

                    return;
                }

                $resolver = app(CapabilityResolver::class);

                if (! $resolver->can($user, $targetTenant, Capabilities::TENANT_SYNC)) {
                    Notification::make()
                        ->title('Permission denied')
                        ->danger()
                        ->send();

                    return;
                }

                $service = app(BaselineCompareService::class);
                $result = $service->startCompare($targetTenant, $user);

                if (! ($result['ok'] ?? false)) {
                    $reasonCode = is_string($result['reason_code'] ?? null) ? (string) $result['reason_code'] : 'unknown';
                    $translation = is_array($result['reason_translation'] ?? null) ? $result['reason_translation'] : [];

                    $message = is_string($translation['short_explanation'] ?? null) && trim((string) $translation['short_explanation']) !== ''
                        ? trim((string) $translation['short_explanation'])
                        : match ($reasonCode) {
                            BaselineReasonCodes::COMPARE_ROLLOUT_DISABLED => 'Full-content baseline compare is currently disabled for controlled rollout.',
                            BaselineReasonCodes::COMPARE_PROFILE_NOT_ACTIVE => 'This baseline profile is not active.',
                            BaselineReasonCodes::COMPARE_NO_ACTIVE_SNAPSHOT,
                            BaselineReasonCodes::COMPARE_NO_CONSUMABLE_SNAPSHOT => 'This baseline profile has no complete snapshot available for compare yet.',
                            BaselineReasonCodes::COMPARE_SNAPSHOT_BUILDING => 'The latest baseline capture is still building. Compare will be available after it completes.',
                            BaselineReasonCodes::COMPARE_SNAPSHOT_INCOMPLETE => 'The latest baseline capture is incomplete. Capture a new baseline before comparing.',
                            BaselineReasonCodes::COMPARE_SNAPSHOT_SUPERSEDED => 'A newer complete snapshot is current. Compare uses the latest complete baseline only.',
                            BaselineReasonCodes::COMPARE_INVALID_SCOPE => 'This baseline profile has an invalid governed-subject scope. Review the baseline definition before comparing.',
                            BaselineReasonCodes::COMPARE_UNSUPPORTED_SCOPE => 'This baseline profile includes governed subjects that are not currently supported for compare.',
                            BaselineReasonCodes::COMPARE_MIXED_SCOPE => 'This baseline profile mixes governed subjects that require different compare strategies. Narrow the selection before comparing.',
                            default => 'Reason: '.str_replace('.', ' ', $reasonCode),
                        };

                    Notification::make()
                        ->title('Cannot start comparison')
                        ->body($message)
                        ->danger()
                        ->send();

                    return;
                }

                $run = $result['run'] ?? null;

                if (! $run instanceof \App\Models\OperationRun) {
                    Notification::make()
                        ->title('Cannot start comparison')
                        ->body('Reason: missing operation run')
                        ->danger()
                        ->send();

                    return;
                }

                $viewAction = Action::make('view_run')
                    ->label('Open operation')
                    ->url(OperationRunLinks::view($run, $targetTenant));

                if (! $run->wasRecentlyCreated && in_array((string) $run->status, ['queued', 'running'], true)) {
                    OpsUxBrowserEvents::dispatchRunEnqueued($this);

                    OperationUxPresenter::alreadyQueuedToast((string) $run->type)
                        ->actions([$viewAction])
                        ->send();

                    return;
                }

                OpsUxBrowserEvents::dispatchRunEnqueued($this);

                OperationUxPresenter::queuedToast((string) $run->type)
                    ->actions([$viewAction])
                    ->send();
            });
    }

    private function compareAssignedTenantsAction(): Action
    {
        $action = Action::make('compareAssignedTenants')
            ->label('Compare assigned environments')
            ->icon('heroicon-o-play')
            ->requiresConfirmation()
            ->modalHeading('Compare assigned environments')
            ->modalDescription('Simulation only. This starts the normal environment-owned baseline compare path for the visible assigned set. No workspace umbrella run is created.')
            ->disabled(fn (): bool => $this->compareAssignedTenantsDisabledReason() !== null)
            ->tooltip(fn (): ?string => $this->compareAssignedTenantsDisabledReason())
            ->action(function (): void {
                $user = auth()->user();

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

                /** @var BaselineProfile $profile */
                $profile = $this->getRecord();
                $result = app(BaselineCompareService::class)->startCompareForVisibleAssignments($profile, $user);
                $summary = sprintf(
                    '%d queued, %d already queued, %d blocked across %d visible assigned tenant%s.',
                    (int) $result['queuedCount'],
                    (int) $result['alreadyQueuedCount'],
                    (int) $result['blockedCount'],
                    (int) $result['visibleAssignedTenantCount'],
                    (int) $result['visibleAssignedTenantCount'] === 1 ? '' : 's',
                );

                if ((int) $result['queuedCount'] > 0 || (int) $result['alreadyQueuedCount'] > 0) {
                    OpsUxBrowserEvents::dispatchRunEnqueued($this);

                    $toast = (int) $result['queuedCount'] > 0
                        ? OperationUxPresenter::queuedToast(OperationRunType::BaselineCompare->value)
                        : OperationUxPresenter::alreadyQueuedToast(OperationRunType::BaselineCompare->value);

                    $toast
                        ->body($summary.' Open Operations for progress and next steps.')
                        ->actions([
                            Action::make('open_operations')
                                ->label('Open operations')
                                ->url(OperationRunLinks::index(allTenants: true)),
                        ])
                        ->send();

                    return;
                }

                Notification::make()
                    ->title('No baseline compares were started')
                    ->body($summary)
                    ->warning()
                    ->send();
            });

        return WorkspaceUiEnforcement::forAction(
            $action,
            fn (): ?Workspace => Workspace::query()->whereKey((int) $this->getRecord()->workspace_id)->first(),
        )
            ->requireCapability(Capabilities::WORKSPACE_BASELINES_MANAGE)
            ->preserveDisabled()
            ->tooltip('You need workspace baseline manage access to compare the visible assigned set.')
            ->apply();
    }

    /**
     * @return array<int, string>
     */
    private function getWorkspaceTenantOptions(): array
    {
        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());

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

        return ManagedEnvironment::query()
            ->where('workspace_id', $workspaceId)
            ->orderBy('name')
            ->pluck('name', 'id')
            ->all();
    }

    /**
     * @return array<int, string>
     */
    private function getEligibleCompareTenantOptions(): array
    {
        $user = auth()->user();

        if (! $user instanceof User) {
            return [];
        }

        /** @var BaselineProfile $profile */
        $profile = $this->getRecord();

        $tenantIds = BaselineTenantAssignment::query()
            ->where('workspace_id', (int) $profile->workspace_id)
            ->where('baseline_profile_id', (int) $profile->getKey())
            ->pluck('managed_environment_id')
            ->all();

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

        $resolver = app(CapabilityResolver::class);

        $options = [];

        $tenants = ManagedEnvironment::query()
            ->where('workspace_id', (int) $profile->workspace_id)
            ->whereIn('id', $tenantIds)
            ->orderBy('name')
            ->get(['id', 'name']);

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

            if (! $resolver->can($user, $tenant, Capabilities::TENANT_SYNC)) {
                continue;
            }

            $options[(int) $tenant->getKey()] = (string) $tenant->name;
        }

        return $options;
    }

    private function hasManageCapability(): bool
    {
        $user = auth()->user();

        if (! $user instanceof User) {
            return false;
        }

        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());

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

        $workspace = Workspace::query()->whereKey($workspaceId)->first();

        if (! $workspace instanceof Workspace) {
            return false;
        }

        $resolver = app(\App\Services\Auth\WorkspaceCapabilityResolver::class);

        return $resolver->isMember($user, $workspace)
            && $resolver->can($user, $workspace, Capabilities::WORKSPACE_BASELINES_MANAGE);
    }

    private function profileHasConsumableSnapshot(): bool
    {
        /** @var BaselineProfile $profile */
        $profile = $this->getRecord();

        return $profile->resolveCurrentConsumableSnapshot() !== null;
    }

    private function shouldHideCaptureAction(): bool
    {
        if (! $this->profileHasConsumableSnapshot()) {
            return false;
        }

        return $this->getEligibleCompareTenantOptions() !== [];
    }

    private function compareAssignedTenantsDisabledReason(): ?string
    {
        /** @var BaselineProfile $profile */
        $profile = $this->getRecord();

        if (! $this->profileHasConsumableSnapshot()) {
            return 'Capture a complete baseline snapshot before comparing assigned environments.';
        }

        if ($this->visibleAssignedTenantCount($profile) === 0) {
            return 'No visible assigned environments are available for compare.';
        }

        return null;
    }

    private function visibleAssignedTenantCount(BaselineProfile $profile): int
    {
        $user = auth()->user();

        if (! $user instanceof User) {
            return 0;
        }

        $tenantIds = BaselineTenantAssignment::query()
            ->where('workspace_id', (int) $profile->workspace_id)
            ->where('baseline_profile_id', (int) $profile->getKey())
            ->pluck('managed_environment_id')
            ->all();

        if ($tenantIds === []) {
            return 0;
        }

        $resolver = app(CapabilityResolver::class);

        return ManagedEnvironment::query()
            ->where('workspace_id', (int) $profile->workspace_id)
            ->whereIn('id', $tenantIds)
            ->get(['id'])
            ->filter(fn (ManagedEnvironment $tenant): bool => $resolver->can($user, $tenant, Capabilities::TENANT_VIEW))
            ->count();
    }
}
