<?php

declare(strict_types=1);

namespace App\Filament\System\Pages\Ops;

use App\Models\AuditLog;
use App\Models\OperationalControlActivation;
use App\Models\PlatformUser;
use App\Models\ManagedEnvironment;
use App\Models\Workspace;
use App\Services\Audit\AuditRecorder;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Support\Audit\AuditActionId;
use App\Support\Audit\AuditActorSnapshot;
use App\Support\Audit\AuditTargetSnapshot;
use App\Support\Auth\PlatformCapabilities;
use App\Support\OperationalControls\OperationalControlCatalog;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use Filament\Actions\Action;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Radio;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Arr;
use Illuminate\Validation\ValidationException;

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

    protected static ?string $title = 'Operational Controls';

    protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-pause-circle';

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

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

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

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

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

        return $user->hasCapability(PlatformCapabilities::ACCESS_SYSTEM_PANEL)
            && $user->hasCapability(PlatformCapabilities::OPS_CONTROLS_MANAGE);
    }

    public function getHeader(): ?View
    {
        return view('filament.system.pages.ops.partials.controls-header', [
            'breadcrumbs' => filament()->hasBreadcrumbs() ? $this->getBreadcrumbs() : [],
            'heading' => $this->getHeading(),
            'subheading' => $this->getSubheading(),
        ]);
    }

    /**
     * @return array<Action>
     */
    protected function getHeaderActions(): array
    {
        return [
            $this->pauseRestoreExecuteAction(),
            $this->resumeRestoreExecuteAction(),
            $this->viewHistoryRestoreExecuteAction(),
            $this->pauseAiExecutionAction(),
            $this->resumeAiExecutionAction(),
            $this->viewHistoryAiExecutionAction(),
        ];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function controlCards(): array
    {
        $catalog = app(OperationalControlCatalog::class);

        return array_map(
            fn (string $controlKey): array => $this->controlSummary($controlKey),
            $catalog->keys(),
        );
    }

    /**
     * @return array<string, mixed>
     */
    public function controlSummary(string $controlKey): array
    {
        $definition = app(OperationalControlCatalog::class)->definition($controlKey);
        $activations = $this->activeActivationsForControl($controlKey);

        $effectiveState = $activations->isEmpty() ? 'enabled' : 'paused';
        $stateLabel = match (true) {
            $activations->contains(fn (OperationalControlActivation $activation): bool => $activation->scope_type === 'global') => 'Paused globally',
            $activations->isNotEmpty() => sprintf('Workspace pauses active (%d)', $activations->where('scope_type', 'workspace')->count()),
            default => 'Enabled',
        };

        return [
            'control_key' => $controlKey,
            'action_slug' => $this->actionSlug($controlKey),
            'label' => (string) $definition['label'],
            'effective_state' => $effectiveState,
            'state_label' => $stateLabel,
            'supported_scopes' => $definition['supported_scopes'],
            'affected_surfaces' => $definition['affected_surfaces'],
            'active_activations' => $activations
                ->map(fn (OperationalControlActivation $activation): array => $this->activationSummary($activation))
                ->values()
                ->all(),
            'history_count' => $this->recentAuditEventsForControl($controlKey)->count(),
        ];
    }

    /**
     * @return array{control_key: string, scope_type: string, workspace_id: ?int, workspace_count: int, tenant_count: int, summary: string}
     */
    public function scopeImpactPreview(string $controlKey, string $scopeType, ?int $workspaceId): array
    {
        $label = app(OperationalControlCatalog::class)->label($controlKey);

        if ($scopeType === 'workspace') {
            $workspace = is_int($workspaceId)
                ? Workspace::query()->whereKey($workspaceId)->first()
                : null;

            if (! $workspace instanceof Workspace) {
                return [
                    'control_key' => $controlKey,
                    'scope_type' => $scopeType,
                    'workspace_id' => null,
                    'workspace_count' => 0,
                    'tenant_count' => 0,
                    'summary' => 'Select a workspace to preview the scope impact.',
                ];
            }

            $tenantCount = ManagedEnvironment::query()
                ->where('workspace_id', (int) $workspace->getKey())
                ->where('slug', '!=', 'platform')
                ->count();

            return [
                'control_key' => $controlKey,
                'scope_type' => $scopeType,
                'workspace_id' => (int) $workspace->getKey(),
                'workspace_count' => 1,
                'tenant_count' => $tenantCount,
                'summary' => sprintf('%s will affect workspace %s and %d %s.', $label, $workspace->name, $tenantCount, $tenantCount === 1 ? 'tenant' : 'tenants'),
            ];
        }

        $tenantCount = ManagedEnvironment::query()
            ->where('slug', '!=', 'platform')
            ->count();

        $workspaceCount = ManagedEnvironment::query()
            ->where('slug', '!=', 'platform')
            ->distinct('workspace_id')
            ->count('workspace_id');

        return [
            'control_key' => $controlKey,
            'scope_type' => 'global',
            'workspace_id' => null,
            'workspace_count' => $workspaceCount,
            'tenant_count' => $tenantCount,
            'summary' => sprintf('%s will affect %d %s across %d %s.', $label, $workspaceCount, $workspaceCount === 1 ? 'workspace' : 'workspaces', $tenantCount, $tenantCount === 1 ? 'tenant' : 'tenants'),
        ];
    }

    public function pauseRestoreExecuteAction(): Action
    {
        return $this->pauseActionFor('restore.execute');
    }

    public function resumeRestoreExecuteAction(): Action
    {
        return $this->resumeActionFor('restore.execute');
    }

    public function viewHistoryRestoreExecuteAction(): Action
    {
        return $this->historyActionFor('restore.execute');
    }

    public function pauseAiExecutionAction(): Action
    {
        return $this->pauseActionFor('ai.execution');
    }

    public function resumeAiExecutionAction(): Action
    {
        return $this->resumeActionFor('ai.execution');
    }

    public function viewHistoryAiExecutionAction(): Action
    {
        return $this->historyActionFor('ai.execution');
    }

    private function pauseActionFor(string $controlKey): Action
    {
        $label = app(OperationalControlCatalog::class)->label($controlKey);

        return Action::make('pause_'.$this->actionSlug($controlKey))
            ->label('Pause '.$label)
            ->icon('heroicon-o-pause')
            ->color('danger')
            ->requiresConfirmation()
            ->modalHeading('Pause '.$label)
            ->modalDescription('Review the scope impact, reason, and optional expiry before confirming this control change.')
            ->form($this->pauseFormSchema($controlKey))
            ->action(function (array $data, AuditRecorder $auditRecorder, WorkspaceAuditLogger $workspaceAuditLogger) use ($controlKey, $label): void {
                $actor = $this->controlsActor();
                [$scopeType, $workspace, $reasonText, $expiresAt] = $this->normalizePauseInput($controlKey, $data);

                $scopeQuery = $this->activationScopeQuery($controlKey, $scopeType, $workspace);

                (clone $scopeQuery)
                    ->whereNotNull('expires_at')
                    ->where('expires_at', '<=', now())
                    ->delete();

                $activation = (clone $scopeQuery)->notExpired()->first();
                $auditAction = $activation instanceof OperationalControlActivation
                    ? AuditActionId::OperationalControlUpdated
                    : AuditActionId::OperationalControlPaused;

                if ($activation instanceof OperationalControlActivation) {
                    $activation->fill([
                        'reason_text' => $reasonText,
                        'expires_at' => $expiresAt,
                        'updated_by_platform_user_id' => (int) $actor->getKey(),
                    ])->save();
                } else {
                    $activation = OperationalControlActivation::query()->create([
                        'control_key' => $controlKey,
                        'scope_type' => $scopeType,
                        'workspace_id' => $workspace instanceof Workspace ? (int) $workspace->getKey() : null,
                        'reason_text' => $reasonText,
                        'expires_at' => $expiresAt,
                        'created_by_platform_user_id' => (int) $actor->getKey(),
                    ]);
                }

                $this->recordControlMutation(
                    auditAction: $auditAction,
                    activation: $activation,
                    actor: $actor,
                    auditRecorder: $auditRecorder,
                    workspaceAuditLogger: $workspaceAuditLogger,
                );

                Notification::make()
                    ->title(sprintf('%s %s', $label, $auditAction === AuditActionId::OperationalControlPaused ? 'paused' : 'updated'))
                    ->success()
                    ->send();
            });
    }

    private function resumeActionFor(string $controlKey): Action
    {
        $label = app(OperationalControlCatalog::class)->label($controlKey);

        return Action::make('resume_'.$this->actionSlug($controlKey))
            ->label('Resume '.$label)
            ->icon('heroicon-o-play')
            ->color('gray')
            ->requiresConfirmation()
            ->modalHeading('Resume '.$label)
            ->modalDescription('Remove the selected pause so new starts can proceed again.')
            ->form($this->resumeFormSchema($controlKey))
            ->action(function (array $data, AuditRecorder $auditRecorder, WorkspaceAuditLogger $workspaceAuditLogger) use ($controlKey, $label): void {
                $actor = $this->controlsActor();
                [$scopeType, $workspace] = $this->normalizeResumeInput($controlKey, $data);

                $activation = $this->activationScopeQuery($controlKey, $scopeType, $workspace)
                    ->notExpired()
                    ->first();

                if (! $activation instanceof OperationalControlActivation) {
                    Notification::make()
                        ->title(sprintf('%s already enabled', $label))
                        ->warning()
                        ->send();

                    return;
                }

                $activationSnapshot = $activation->replicate();
                $activationSnapshot->forceFill($activation->getAttributes());
                $activation->delete();

                $this->recordControlMutation(
                    auditAction: AuditActionId::OperationalControlResumed,
                    activation: $activationSnapshot,
                    actor: $actor,
                    auditRecorder: $auditRecorder,
                    workspaceAuditLogger: $workspaceAuditLogger,
                );

                Notification::make()
                    ->title($label.' resumed')
                    ->success()
                    ->send();
            });
    }

    private function historyActionFor(string $controlKey): Action
    {
        $label = app(OperationalControlCatalog::class)->label($controlKey);

        return Action::make('view_history_'.$this->actionSlug($controlKey))
            ->label('View '.$label.' history')
            ->link()
            ->modalHeading($label.' history')
            ->modalSubmitAction(false)
            ->modalCancelActionLabel('Close')
            ->modalContent(fn () => view('filament.system.pages.ops.partials.operational-control-history', [
                'events' => $this->recentAuditEventsForControl($controlKey),
                'label' => $label,
            ]));
    }

    /**
     * @return array<int, \Filament\Schemas\Components\Component>
     */
    private function pauseFormSchema(string $controlKey): array
    {
        return [
            Radio::make('scope_type')
                ->label('Scope')
                ->options($this->scopeOptions($controlKey))
                ->default($this->defaultScopeFor($controlKey))
                ->live()
                ->required(),

            Select::make('workspace_id')
                ->label('Workspace')
                ->searchable()
                ->visible(fn (callable $get): bool => $get('scope_type') === 'workspace')
                ->required(fn (callable $get): bool => $get('scope_type') === 'workspace')
                ->live()
                ->getSearchResultsUsing(function (string $search): array {
                    return Workspace::query()
                        ->where('name', 'like', "%{$search}%")
                        ->orderBy('name')
                        ->limit(25)
                        ->pluck('name', 'id')
                        ->all();
                })
                ->getOptionLabelUsing(function ($value): ?string {
                    if (! is_numeric($value)) {
                        return null;
                    }

                    return Workspace::query()->whereKey((int) $value)->value('name');
                }),

            Textarea::make('reason_text')
                ->label('Reason')
                ->required()
                ->minLength(5)
                ->maxLength(500)
                ->rows(4),

            DateTimePicker::make('expires_at')
                ->label('Expires at')
                ->seconds(false)
                ->nullable(),

            Placeholder::make('scope_preview')
                ->label('Scope impact preview')
                ->content(function (callable $get) use ($controlKey): string {
                    $preview = $this->scopeImpactPreview(
                        $controlKey,
                        (string) ($get('scope_type') ?? 'global'),
                        is_numeric($get('workspace_id')) ? (int) $get('workspace_id') : null,
                    );

                    return (string) $preview['summary'];
                }),
        ];
    }

    /**
     * @return array<int, \Filament\Schemas\Components\Component>
     */
    private function resumeFormSchema(string $controlKey): array
    {
        return [
            Radio::make('scope_type')
                ->label('Scope')
                ->options($this->scopeOptions($controlKey))
                ->default($this->defaultScopeFor($controlKey))
                ->live()
                ->required(),

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

                    return Workspace::query()->whereKey((int) $value)->value('name');
                }),

            Placeholder::make('scope_preview')
                ->label('Resume impact preview')
                ->content(function (callable $get) use ($controlKey): string {
                    $preview = $this->scopeImpactPreview(
                        $controlKey,
                        (string) ($get('scope_type') ?? 'global'),
                        is_numeric($get('workspace_id')) ? (int) $get('workspace_id') : null,
                    );

                    return (string) $preview['summary'];
                }),
        ];
    }

    private function controlsActor(): PlatformUser
    {
        $actor = auth('platform')->user();

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

        if (! $actor->hasCapability(PlatformCapabilities::OPS_CONTROLS_MANAGE)) {
            abort(403);
        }

        return $actor;
    }

    /**
     * @return array{0: string, 1: ?Workspace, 2: string, 3: ?CarbonInterface}
     */
    private function normalizePauseInput(string $controlKey, array $data): array
    {
        [$scopeType, $workspace] = $this->resolveScopeInput($controlKey, $data);
        $reasonText = trim((string) ($data['reason_text'] ?? ''));

        if ($reasonText === '') {
            throw ValidationException::withMessages([
                'reason_text' => 'A reason is required.',
            ]);
        }

        $expiresAt = null;

        if (filled($data['expires_at'] ?? null)) {
            $expiresAt = Carbon::parse((string) $data['expires_at']);

            if ($expiresAt->lessThanOrEqualTo(now())) {
                throw ValidationException::withMessages([
                    'expires_at' => 'Expiry must be in the future.',
                ]);
            }
        }

        return [$scopeType, $workspace, $reasonText, $expiresAt];
    }

    /**
     * @return array{0: string, 1: ?Workspace}
     */
    private function normalizeResumeInput(string $controlKey, array $data): array
    {
        return $this->resolveScopeInput($controlKey, $data);
    }

    /**
     * @return array{0: string, 1: ?Workspace}
     */
    private function resolveScopeInput(string $controlKey, array $data): array
    {
        $scopeType = (string) ($data['scope_type'] ?? 'global');
        $supportedScopes = app(OperationalControlCatalog::class)->definition($controlKey)['supported_scopes'] ?? ['global'];

        if (! in_array($scopeType, $supportedScopes, true)) {
            throw ValidationException::withMessages([
                'scope_type' => 'Invalid scope selected.',
            ]);
        }

        if ($scopeType === 'global') {
            return [$scopeType, null];
        }

        $workspaceId = $data['workspace_id'] ?? null;

        if (! is_numeric($workspaceId)) {
            throw ValidationException::withMessages([
                'workspace_id' => 'A workspace is required for workspace scope.',
            ]);
        }

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

        if (! $workspace instanceof Workspace) {
            throw ValidationException::withMessages([
                'workspace_id' => 'The selected workspace could not be found.',
            ]);
        }

        return [$scopeType, $workspace];
    }

    /**
     * @return array<string, string>
     */
    private function scopeOptions(string $controlKey): array
    {
        $supportedScopes = app(OperationalControlCatalog::class)->definition($controlKey)['supported_scopes'];

        return Arr::only([
            'global' => 'Global',
            'workspace' => 'One workspace',
        ], $supportedScopes);
    }

    private function defaultScopeFor(string $controlKey): string
    {
        $supportedScopes = app(OperationalControlCatalog::class)->definition($controlKey)['supported_scopes'];

        return $supportedScopes[0] ?? 'global';
    }

    private function activationScopeQuery(string $controlKey, string $scopeType, ?Workspace $workspace): \Illuminate\Database\Eloquent\Builder
    {
        $query = OperationalControlActivation::query()
            ->forControl($controlKey)
            ->where('scope_type', $scopeType);

        if ($scopeType === 'workspace') {
            $query->where('workspace_id', (int) $workspace?->getKey());
        } else {
            $query->whereNull('workspace_id');
        }

        return $query;
    }

    private function recordControlMutation(
        AuditActionId $auditAction,
        OperationalControlActivation $activation,
        PlatformUser $actor,
        AuditRecorder $auditRecorder,
        WorkspaceAuditLogger $workspaceAuditLogger,
    ): void {
        $label = app(OperationalControlCatalog::class)->label((string) $activation->control_key);
        $summary = sprintf('%s %s', $label, match ($auditAction) {
            AuditActionId::OperationalControlPaused => 'paused',
            AuditActionId::OperationalControlUpdated => 'updated',
            AuditActionId::OperationalControlResumed => 'resumed',
            default => 'changed',
        });

        $metadata = array_filter([
            'control_key' => (string) $activation->control_key,
            'scope_type' => (string) $activation->scope_type,
            'workspace_id' => is_numeric($activation->workspace_id) ? (int) $activation->workspace_id : null,
            'reason_text' => $activation->reason_text,
            'expires_at' => $activation->expires_at?->toIso8601String(),
            'actor_id' => (int) $actor->getKey(),
        ], static fn (mixed $value): bool => $value !== null && $value !== '');

        if ((string) $activation->scope_type === 'global') {
            $auditRecorder->record(
                action: $auditAction,
                context: ['metadata' => $metadata],
                actor: AuditActorSnapshot::platform($actor),
                target: new AuditTargetSnapshot(
                    type: 'operational_control',
                    id: (string) $activation->getKey(),
                    label: $label,
                ),
                outcome: 'success',
                summary: $summary,
            );

            return;
        }

        $workspace = Workspace::query()->whereKey((int) $activation->workspace_id)->firstOrFail();

        $workspaceAuditLogger->log(
            workspace: $workspace,
            action: $auditAction,
            context: ['metadata' => $metadata],
            actor: $actor,
            status: 'success',
            resourceType: 'operational_control',
            resourceId: (string) $activation->getKey(),
            targetLabel: $label,
            summary: $summary,
        );
    }

    /**
     * @return Collection<int, OperationalControlActivation>
     */
    private function activeActivationsForControl(string $controlKey): Collection
    {
        return OperationalControlActivation::query()
            ->forControl($controlKey)
            ->notExpired()
            ->with(['workspace', 'createdBy', 'updatedBy'])
            ->orderByRaw("CASE WHEN scope_type = 'global' THEN 0 ELSE 1 END")
            ->orderBy('workspace_id')
            ->orderBy('id')
            ->get();
    }

    /**
     * @return array<string, mixed>
     */
    private function activationSummary(OperationalControlActivation $activation): array
    {
        $owner = $activation->updatedBy ?? $activation->createdBy;
        $workspaceName = $activation->workspace?->name;

        return [
            'id' => (int) $activation->getKey(),
            'scope_type' => (string) $activation->scope_type,
            'scope_label' => (string) $activation->scope_type === 'global'
                ? 'Global'
                : sprintf('Workspace: %s', $workspaceName ?? '#'.(int) $activation->workspace_id),
            'workspace_id' => is_numeric($activation->workspace_id) ? (int) $activation->workspace_id : null,
            'workspace_name' => $workspaceName,
            'reason_text' => (string) $activation->reason_text,
            'expires_at' => $activation->expires_at?->toIso8601String(),
            'expires_label' => $activation->expires_at?->diffForHumans() ?? 'No expiry',
            'owner_name' => $owner?->name ?: $owner?->email ?: 'Unknown operator',
        ];
    }

    /**
     * @return Collection<int, AuditLog>
     */
    private function recentAuditEventsForControl(string $controlKey): Collection
    {
        return AuditLog::query()
            ->where('metadata->control_key', $controlKey)
            ->whereIn('action', [
                AuditActionId::OperationalControlPaused->value,
                AuditActionId::OperationalControlUpdated->value,
                AuditActionId::OperationalControlResumed->value,
                AuditActionId::OperationalControlExecutionBlocked->value,
            ])
            ->latestFirst()
            ->limit(10)
            ->get();
    }

    private function actionSlug(string $controlKey): string
    {
        return str_replace('.', '_', $controlKey);
    }
}