<?php

declare(strict_types=1);

namespace App\Filament\Resources;

use App\Filament\Clusters\Monitoring\AlertsCluster;
use App\Filament\Resources\AlertRuleResource\Pages;
use App\Models\AlertDestination;
use App\Models\AlertRule;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Support\Audit\AuditActionId;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
use App\Support\Workspaces\WorkspaceContext;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Filament\Facades\Filament;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use UnitEnum;

class AlertRuleResource extends Resource
{
    protected static bool $isScopedToTenant = false;

    protected static ?string $model = AlertRule::class;

    protected static ?string $slug = 'alert-rules';

    protected static ?string $cluster = AlertsCluster::class;

    protected static ?int $navigationSort = 2;

    protected static bool $isGloballySearchable = false;

    protected static ?string $recordTitleAttribute = 'name';

    protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-funnel';

    protected static string|UnitEnum|null $navigationGroup = 'Monitoring';

    protected static ?string $navigationLabel = 'Alert rules';

    public static function shouldRegisterNavigation(): bool
    {
        if (Filament::getCurrentPanel()?->getId() !== 'admin') {
            return false;
        }

        return parent::shouldRegisterNavigation();
    }

    public static function canViewAny(): bool
    {
        $user = auth()->user();

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

        return $user->can('viewAny', AlertRule::class);
    }

    public static function canCreate(): bool
    {
        $user = auth()->user();

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

        return $user->can('create', AlertRule::class);
    }

    public static function canEdit(Model $record): bool
    {
        $user = auth()->user();

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

        return $user->can('update', $record);
    }

    public static function canDelete(Model $record): bool
    {
        $user = auth()->user();

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

        return $user->can('delete', $record);
    }

    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forResource(ActionSurfaceProfile::CrudListAndEdit)
            ->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions include capability-gated create.')
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value)
            ->satisfy(ActionSurfaceSlot::ListRowMoreMenu, 'Secondary row actions are grouped under "More".')
            ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'No bulk mutations are exposed for alert rules in v1.')
            ->satisfy(ActionSurfaceSlot::ListEmptyState, 'List page defines an empty-state create CTA.')
            ->satisfy(ActionSurfaceSlot::DetailHeader, 'Edit page provides default save/cancel actions.');
    }

    public static function getEloquentQuery(): Builder
    {
        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());

        return parent::getEloquentQuery()
            ->with('destinations')
            ->when(
                $workspaceId !== null,
                fn (Builder $query): Builder => $query->where('workspace_id', (int) $workspaceId),
            )
            ->when(
                $workspaceId === null,
                fn (Builder $query): Builder => $query->whereRaw('1 = 0'),
            );
    }

    public static function form(Schema $schema): Schema
    {
        return $schema
            ->schema([
                Section::make('Rule')
                    ->schema([
                        TextInput::make('name')
                            ->required()
                            ->maxLength(255),
                        Toggle::make('is_enabled')
                            ->label('Enabled')
                            ->default(true),
                        Select::make('event_type')
                            ->required()
                            ->options(self::eventTypeOptions())
                            ->native(false),
                        Select::make('minimum_severity')
                            ->required()
                            ->options(self::severityOptions())
                            ->native(false),
                    ]),
                Section::make('Applies to')
                    ->schema([
                        Select::make('tenant_scope_mode')
                            ->label('Applies to tenants')
                            ->required()
                            ->options([
                                AlertRule::TENANT_SCOPE_ALL => 'All tenants',
                                AlertRule::TENANT_SCOPE_ALLOWLIST => 'Selected tenants',
                            ])
                            ->default(AlertRule::TENANT_SCOPE_ALL)
                            ->native(false)
                            ->live()
                            ->helperText('This rule is workspace-wide. Use this to limit where it applies.'),
                        Select::make('tenant_allowlist')
                            ->label('Selected tenants')
                            ->multiple()
                            ->options(self::tenantOptions())
                            ->visible(fn (Get $get): bool => $get('tenant_scope_mode') === AlertRule::TENANT_SCOPE_ALLOWLIST)
                            ->native(false)
                            ->helperText('Only these tenants will trigger this rule.'),
                    ]),
                Section::make('Delivery')
                    ->schema([
                        TextInput::make('cooldown_seconds')
                            ->label('Cooldown (seconds)')
                            ->numeric()
                            ->minValue(0)
                            ->nullable(),
                        Toggle::make('quiet_hours_enabled')
                            ->label('Enable quiet hours')
                            ->default(false)
                            ->live(),
                        TextInput::make('quiet_hours_start')
                            ->label('Quiet hours start')
                            ->type('time')
                            ->visible(fn (Get $get): bool => (bool) $get('quiet_hours_enabled')),
                        TextInput::make('quiet_hours_end')
                            ->label('Quiet hours end')
                            ->type('time')
                            ->visible(fn (Get $get): bool => (bool) $get('quiet_hours_enabled')),
                        Select::make('quiet_hours_timezone')
                            ->label('Quiet hours timezone')
                            ->options(self::timezoneOptions())
                            ->searchable()
                            ->native(false)
                            ->visible(fn (Get $get): bool => (bool) $get('quiet_hours_enabled')),
                        Select::make('destination_ids')
                            ->label('Destinations')
                            ->multiple()
                            ->required()
                            ->options(self::destinationOptions())
                            ->native(false),
                    ]),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->defaultSort('name')
            ->paginated(\App\Support\Filament\TablePaginationProfiles::resource())
            ->recordUrl(fn (AlertRule $record): ?string => static::canEdit($record)
                ? static::getUrl('edit', ['record' => $record])
                : null)
            ->columns([
                TextColumn::make('name')
                    ->searchable()
                    ->sortable(),
                TextColumn::make('event_type')
                    ->label('Event')
                    ->badge()
                    ->formatStateUsing(fn (?string $state): string => self::eventTypeLabel((string) $state)),
                TextColumn::make('minimum_severity')
                    ->label('Min severity')
                    ->badge()
                    ->formatStateUsing(fn (?string $state): string => self::severityOptions()[(string) $state] ?? ucfirst((string) $state)),
                TextColumn::make('destinations_count')
                    ->label('Destinations')
                    ->counts('destinations'),
                TextColumn::make('is_enabled')
                    ->label('Enabled')
                    ->badge()
                    ->formatStateUsing(fn (bool $state): string => $state ? 'Yes' : 'No')
                    ->color(fn (bool $state): string => $state ? 'success' : 'gray'),
            ])
            ->actions([
                ActionGroup::make([
                    Action::make('toggle_enabled')
                        ->label(fn (AlertRule $record): string => $record->is_enabled ? 'Disable' : 'Enable')
                        ->icon(fn (AlertRule $record): string => $record->is_enabled ? 'heroicon-o-pause' : 'heroicon-o-play')
                        ->requiresConfirmation()
                        ->action(function (AlertRule $record): void {
                            $user = auth()->user();

                            if (! $user instanceof User || ! $user->can('update', $record)) {
                                throw new AuthorizationException;
                            }

                            $enabled = ! (bool) $record->is_enabled;
                            $record->forceFill([
                                'is_enabled' => $enabled,
                            ])->save();

                            $actionId = $enabled
                                ? AuditActionId::AlertRuleEnabled
                                : AuditActionId::AlertRuleDisabled;

                            self::audit($record, $actionId, [
                                'alert_rule_id' => (int) $record->getKey(),
                                'name' => (string) $record->name,
                                'event_type' => (string) $record->event_type,
                                'is_enabled' => $enabled,
                            ]);

                            Notification::make()
                                ->title($enabled ? 'Rule enabled' : 'Rule disabled')
                                ->success()
                                ->send();
                        }),
                    Action::make('delete')
                        ->label('Delete')
                        ->icon('heroicon-o-trash')
                        ->color('danger')
                        ->requiresConfirmation()
                        ->action(function (AlertRule $record): void {
                            $user = auth()->user();

                            if (! $user instanceof User || ! $user->can('delete', $record)) {
                                throw new AuthorizationException;
                            }

                            self::audit($record, AuditActionId::AlertRuleDeleted, [
                                'alert_rule_id' => (int) $record->getKey(),
                                'name' => (string) $record->name,
                                'event_type' => (string) $record->event_type,
                            ]);

                            $record->delete();

                            Notification::make()
                                ->title('Rule deleted')
                                ->success()
                                ->send();
                        }),
                ])->label('More'),
            ])
            ->emptyStateHeading('No alert rules')
            ->emptyStateDescription('Create a rule to route notifications when monitored events fire.')
            ->emptyStateIcon('heroicon-o-bell');
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListAlertRules::route('/'),
            'create' => Pages\CreateAlertRule::route('/create'),
            'edit' => Pages\EditAlertRule::route('/{record}/edit'),
        ];
    }

    /**
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    public static function normalizePayload(array $data): array
    {
        $tenantAllowlist = Arr::wrap($data['tenant_allowlist'] ?? []);
        $tenantAllowlist = array_values(array_unique(array_filter(array_map(static fn (mixed $value): int => (int) $value, $tenantAllowlist))));

        if (($data['tenant_scope_mode'] ?? AlertRule::TENANT_SCOPE_ALL) !== AlertRule::TENANT_SCOPE_ALLOWLIST) {
            $tenantAllowlist = [];
        }

        $quietHoursEnabled = (bool) ($data['quiet_hours_enabled'] ?? false);

        $data['is_enabled'] = (bool) ($data['is_enabled'] ?? true);
        $data['tenant_allowlist'] = $tenantAllowlist;
        $data['cooldown_seconds'] = is_numeric($data['cooldown_seconds'] ?? null) ? (int) $data['cooldown_seconds'] : null;
        $data['quiet_hours_enabled'] = $quietHoursEnabled;

        if (! $quietHoursEnabled) {
            $data['quiet_hours_start'] = null;
            $data['quiet_hours_end'] = null;
            $data['quiet_hours_timezone'] = null;
        }

        return $data;
    }

    /**
     * @param  array<int, int>  $destinationIds
     */
    public static function syncDestinations(AlertRule $record, array $destinationIds): void
    {
        $allowedDestinationIds = AlertDestination::query()
            ->where('workspace_id', (int) $record->workspace_id)
            ->whereIn('id', $destinationIds)
            ->pluck('id')
            ->map(static fn (mixed $value): int => (int) $value)
            ->all();

        $record->destinations()->syncWithPivotValues(
            array_values(array_unique($allowedDestinationIds)),
            ['workspace_id' => (int) $record->workspace_id],
        );
    }

    /**
     * @return array<string, string>
     */
    public static function eventTypeOptions(): array
    {
        return [
            AlertRule::EVENT_HIGH_DRIFT => 'High drift',
            AlertRule::EVENT_COMPARE_FAILED => 'Compare failed',
            AlertRule::EVENT_BASELINE_HIGH_DRIFT => 'Baseline drift',
            AlertRule::EVENT_BASELINE_COMPARE_FAILED => 'Baseline compare failed',
            AlertRule::EVENT_SLA_DUE => 'SLA due',
            AlertRule::EVENT_PERMISSION_MISSING => 'Permission missing',
            AlertRule::EVENT_ENTRA_ADMIN_ROLES_HIGH => 'Entra admin roles (high privilege)',
            AlertRule::EVENT_FINDINGS_ASSIGNED => 'Finding assigned',
            AlertRule::EVENT_FINDINGS_REOPENED => 'Finding reopened',
            AlertRule::EVENT_FINDINGS_DUE_SOON => 'Finding due soon',
            AlertRule::EVENT_FINDINGS_OVERDUE => 'Finding overdue',
        ];
    }

    /**
     * @return array<string, string>
     */
    public static function severityOptions(): array
    {
        return [
            'low' => 'Low',
            'medium' => 'Medium',
            'high' => 'High',
            'critical' => 'Critical',
        ];
    }

    public static function eventTypeLabel(string $eventType): string
    {
        return self::eventTypeOptions()[$eventType] ?? ucfirst(str_replace('_', ' ', $eventType));
    }

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

        if (! is_int($workspaceId)) {
            return [];
        }

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

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

        if (! is_int($workspaceId)) {
            return [];
        }

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

    /**
     * @return array<string, string>
     */
    private static function timezoneOptions(): array
    {
        $identifiers = \DateTimeZone::listIdentifiers();
        sort($identifiers);

        return array_combine($identifiers, $identifiers);
    }

    /**
     * @param  array<string, mixed>  $metadata
     */
    public static function audit(AlertRule $record, AuditActionId $actionId, array $metadata): void
    {
        $workspace = $record->workspace;

        if ($workspace === null) {
            return;
        }

        $actor = auth()->user();

        app(WorkspaceAuditLogger::class)->log(
            workspace: $workspace,
            action: $actionId->value,
            context: [
                'metadata' => $metadata,
            ],
            actor: $actor instanceof User ? $actor : null,
            resourceType: 'alert_rule',
            resourceId: (string) $record->getKey(),
        );
    }
}
