<?php

declare(strict_types=1);

namespace App\Filament\System\Pages\Ops;

use App\Models\OperationRun;
use App\Models\PlatformUser;
use App\Models\Tenant;
use App\Services\Auth\BreakGlassSession;
use App\Services\Runbooks\FindingsLifecycleBackfillRunbookService;
use App\Services\Runbooks\FindingsLifecycleBackfillScope;
use App\Services\Runbooks\RunbookReason;
use App\Services\System\AllowedTenantUniverse;
use App\Support\Auth\PlatformCapabilities;
use App\Support\OpsUx\OperationUxPresenter;
use App\Support\System\SystemOperationRunLinks;
use Filament\Actions\Action;
use Filament\Forms\Components\Radio;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Validation\ValidationException;

class Runbooks extends Page
{
    protected static ?string $navigationLabel = 'Runbooks';

    protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-wrench-screwdriver';

    protected static string|\UnitEnum|null $navigationGroup = 'Ops';

    protected static ?string $slug = 'ops/runbooks';

    protected string $view = 'filament.system.pages.ops.runbooks';

    public string $findingsScopeMode = FindingsLifecycleBackfillScope::MODE_ALL_TENANTS;

    public ?int $findingsTenantId = null;

    public string $scopeMode = FindingsLifecycleBackfillScope::MODE_ALL_TENANTS;

    public ?int $tenantId = null;

    /**
     * @var array{affected_count: int, total_count: int, estimated_tenants?: int|null}|null
     */
    public ?array $findingsPreflight = null;

    /**
     * @var array{affected_count: int, total_count: int, estimated_tenants?: int|null}|null
     */
    public ?array $preflight = null;

    public function findingsScopeLabel(): string
    {
        if ($this->findingsScopeMode === FindingsLifecycleBackfillScope::MODE_ALL_TENANTS) {
            return 'All tenants';
        }

        $tenantName = $this->selectedTenantName($this->findingsTenantId);

        if ($tenantName !== null) {
            return "Single tenant ({$tenantName})";
        }

        return $this->findingsTenantId !== null ? "Single tenant (#{$this->findingsTenantId})" : 'Single tenant';
    }

    public function findingsLastRun(): ?OperationRun
    {
        return $this->lastRunForType(FindingsLifecycleBackfillRunbookService::RUNBOOK_KEY);
    }

    public function selectedTenantName(?int $tenantId): ?string
    {
        if ($tenantId === null) {
            return null;
        }

        return Tenant::query()->whereKey($tenantId)->value('name');
    }

    public static function canAccess(): bool
    {
        $user = auth('platform')->user();

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

        return $user->hasCapability(PlatformCapabilities::OPS_VIEW)
            && $user->hasCapability(PlatformCapabilities::RUNBOOKS_VIEW);
    }

    /**
     * @return array<Action>
     */
    protected function getHeaderActions(): array
    {
        return [
            Action::make('preflight')
                ->label('Preflight')
                ->color('gray')
                ->icon('heroicon-o-magnifying-glass')
                ->form($this->findingsScopeForm())
                ->action(function (array $data, FindingsLifecycleBackfillRunbookService $runbookService): void {
                    $scope = FindingsLifecycleBackfillScope::fromArray([
                        'mode' => $data['scope_mode'] ?? null,
                        'tenant_id' => $data['tenant_id'] ?? null,
                    ]);

                    $this->findingsScopeMode = $scope->mode;
                    $this->findingsTenantId = $scope->tenantId;
                    $this->scopeMode = $scope->mode;
                    $this->tenantId = $scope->tenantId;

                    $this->findingsPreflight = $runbookService->preflight($scope);
                    $this->preflight = $this->findingsPreflight;

                    Notification::make()
                        ->title('Preflight complete')
                        ->success()
                        ->send();
                }),

            Action::make('run')
                ->label('Run…')
                ->icon('heroicon-o-play')
                ->color('danger')
                ->requiresConfirmation()
                ->modalHeading('Run: Rebuild Findings Lifecycle')
                ->modalDescription('This operation may modify customer data. Review the preflight and confirm before running.')
                ->form($this->findingsRunForm())
                ->disabled(fn (): bool => ! is_array($this->findingsPreflight) || (int) ($this->findingsPreflight['affected_count'] ?? 0) <= 0)
                ->action(function (array $data, FindingsLifecycleBackfillRunbookService $runbookService): void {
                    if (! is_array($this->findingsPreflight) || (int) ($this->findingsPreflight['affected_count'] ?? 0) <= 0) {
                        throw ValidationException::withMessages([
                            'preflight' => 'Run preflight first.',
                        ]);
                    }

                    $scope = $this->findingsScopeMode === FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT
                        ? FindingsLifecycleBackfillScope::singleTenant((int) $this->findingsTenantId)
                        : FindingsLifecycleBackfillScope::allTenants();

                    $user = auth('platform')->user();

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

                    if (! $user->hasCapability(PlatformCapabilities::RUNBOOKS_RUN)
                        || ! $user->hasCapability(PlatformCapabilities::RUNBOOKS_FINDINGS_LIFECYCLE_BACKFILL)
                    ) {
                        abort(403);
                    }

                    if ($scope->isAllTenants()) {
                        $typedConfirmation = (string) ($data['typed_confirmation'] ?? '');

                        if ($typedConfirmation !== 'BACKFILL') {
                            throw ValidationException::withMessages([
                                'typed_confirmation' => 'Please type BACKFILL to confirm.',
                            ]);
                        }
                    }

                    $reason = RunbookReason::fromNullableArray([
                        'reason_code' => $data['reason_code'] ?? null,
                        'reason_text' => $data['reason_text'] ?? null,
                    ]);

                    $run = $runbookService->start(
                        scope: $scope,
                        initiator: $user,
                        reason: $reason,
                        source: 'system_ui',
                    );

                    $viewUrl = SystemOperationRunLinks::view($run);

                    $toast = $run->wasRecentlyCreated
                        ? OperationUxPresenter::queuedToast((string) $run->type)->body('The runbook will execute in the background.')
                        : OperationUxPresenter::alreadyQueuedToast((string) $run->type);

                    $toast
                        ->actions([
                            Action::make('view_run')
                                ->label('View run')
                                ->url($viewUrl),
                        ])
                        ->send();
                }),
        ];
    }

    /**
     * @return array<int, \Filament\Schemas\Components\Component>
     */
    private function findingsScopeForm(): array
    {
        return [
            Radio::make('scope_mode')
                ->label('Scope')
                ->options([
                    FindingsLifecycleBackfillScope::MODE_ALL_TENANTS => 'All tenants',
                    FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT => 'Single tenant',
                ])
                ->default($this->findingsScopeMode)
                ->live()
                ->required(),

            Select::make('tenant_id')
                ->label('Tenant')
                ->searchable()
                ->visible(fn (callable $get): bool => $get('scope_mode') === FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT)
                ->required(fn (callable $get): bool => $get('scope_mode') === FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT)
                ->getSearchResultsUsing(function (string $search, AllowedTenantUniverse $universe): array {
                    return $universe
                        ->query()
                        ->where('name', 'like', "%{$search}%")
                        ->orderBy('name')
                        ->limit(25)
                        ->pluck('name', 'id')
                        ->all();
                })
                ->getOptionLabelUsing(function ($value, AllowedTenantUniverse $universe): ?string {
                    if (! is_numeric($value)) {
                        return null;
                    }

                    return $universe
                        ->query()
                        ->whereKey((int) $value)
                        ->value('name');
                }),
        ];
    }

    /**
     * @return array<int, \Filament\Schemas\Components\Component>
     */
    private function findingsRunForm(): array
    {
        return [
            TextInput::make('typed_confirmation')
                ->label('Type BACKFILL to confirm')
                ->visible(fn (): bool => $this->findingsScopeMode === FindingsLifecycleBackfillScope::MODE_ALL_TENANTS)
                ->required(fn (): bool => $this->findingsScopeMode === FindingsLifecycleBackfillScope::MODE_ALL_TENANTS)
                ->in(['BACKFILL'])
                ->validationMessages([
                    'in' => 'Please type BACKFILL to confirm.',
                ]),

            Select::make('reason_code')
                ->label('Reason code')
                ->options(RunbookReason::options())
                ->required(function (BreakGlassSession $breakGlass): bool {
                    return $this->findingsScopeMode === FindingsLifecycleBackfillScope::MODE_ALL_TENANTS || $breakGlass->isActive();
                }),

            Textarea::make('reason_text')
                ->label('Reason')
                ->rows(4)
                ->maxLength(500)
                ->required(function (BreakGlassSession $breakGlass): bool {
                    return $this->findingsScopeMode === FindingsLifecycleBackfillScope::MODE_ALL_TENANTS || $breakGlass->isActive();
                }),
        ];
    }

    private function lastRunForType(string $type): ?OperationRun
    {
        $platformTenant = Tenant::query()->where('external_id', 'platform')->first();

        if (! $platformTenant instanceof Tenant) {
            return null;
        }

        return OperationRun::query()
            ->where('workspace_id', (int) $platformTenant->workspace_id)
            ->where('type', $type)
            ->latest('id')
            ->first();
    }
}
