<?php

declare(strict_types=1);

namespace App\Filament\Resources;

use App\Filament\Clusters\Monitoring\AlertsCluster;
use App\Filament\Resources\AlertDestinationResource\Pages;
use App\Models\AlertDestination;
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\TagsInput;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
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 Illuminate\Validation\ValidationException;
use UnitEnum;

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

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

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

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

    protected static ?int $navigationSort = 3;

    protected static bool $isGloballySearchable = false;

    protected static ?string $recordTitleAttribute = 'name';

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

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

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

    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', AlertDestination::class);
    }

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

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

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

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

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

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

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

        if (! $user instanceof User || ! $record instanceof AlertDestination) {
            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 destinations 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()
            ->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([
                TextInput::make('name')
                    ->required()
                    ->maxLength(255),
                Select::make('type')
                    ->required()
                    ->options(self::typeOptions())
                    ->native(false)
                    ->live(),
                Toggle::make('is_enabled')
                    ->label('Enabled')
                    ->default(true),
                TextInput::make('teams_webhook_url')
                    ->label('Teams webhook URL')
                    ->placeholder('https://...')
                    ->url()
                    ->visible(fn (Get $get): bool => $get('type') === AlertDestination::TYPE_TEAMS_WEBHOOK),
                TagsInput::make('email_recipients')
                    ->label('Email recipients')
                    ->visible(fn (Get $get): bool => $get('type') === AlertDestination::TYPE_EMAIL)
                    ->placeholder('ops@example.com')
                    ->nestedRecursiveRules(['email']),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->defaultSort('name')
            ->paginated(\App\Support\Filament\TablePaginationProfiles::resource())
            ->recordUrl(fn (AlertDestination $record): ?string => static::canEdit($record)
                ? static::getUrl('edit', ['record' => $record])
                : static::getUrl('view', ['record' => $record]))
            ->columns([
                TextColumn::make('name')
                    ->searchable()
                    ->sortable(),
                TextColumn::make('type')
                    ->badge()
                    ->formatStateUsing(fn (?string $state): string => self::typeLabel((string) $state)),
                TextColumn::make('is_enabled')
                    ->label('Enabled')
                    ->badge()
                    ->formatStateUsing(fn (bool $state): string => $state ? 'Yes' : 'No')
                    ->color(fn (bool $state): string => $state ? 'success' : 'gray'),
                TextColumn::make('updated_at')
                    ->since(),
            ])
            ->actions([
                ActionGroup::make([
                    Action::make('toggle_enabled')
                        ->label(fn (AlertDestination $record): string => $record->is_enabled ? 'Disable' : 'Enable')
                        ->icon(fn (AlertDestination $record): string => $record->is_enabled ? 'heroicon-o-pause' : 'heroicon-o-play')
                        ->action(function (AlertDestination $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::AlertDestinationEnabled
                                : AuditActionId::AlertDestinationDisabled;

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

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

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

                            self::audit($record, AuditActionId::AlertDestinationDeleted, [
                                'alert_destination_id' => (int) $record->getKey(),
                                'name' => (string) $record->name,
                                'type' => (string) $record->type,
                            ]);

                            $record->delete();

                            Notification::make()
                                ->title('Destination deleted')
                                ->success()
                                ->send();
                        }),
                ])->label('More'),
            ])
            ->emptyStateActions([
                \Filament\Actions\CreateAction::make()
                    ->label('Create target')
                    ->disabled(fn (): bool => ! static::canCreate()),
            ])
            ->emptyStateHeading('No alert destinations')
            ->emptyStateDescription('Create a destination so alert rules have somewhere to deliver notifications.')
            ->emptyStateIcon('heroicon-o-paper-airplane');
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListAlertDestinations::route('/'),
            'create' => Pages\CreateAlertDestination::route('/create'),
            'view' => Pages\ViewAlertDestination::route('/{record}'),
            'edit' => Pages\EditAlertDestination::route('/{record}/edit'),
        ];
    }

    /**
     * @param  array<string, mixed>  $data
     */
    public static function normalizePayload(array $data, ?AlertDestination $record = null): array
    {
        $type = trim((string) ($data['type'] ?? $record?->type ?? ''));
        $existingConfig = is_array($record?->config ?? null) ? $record->config : [];

        if ($type === AlertDestination::TYPE_TEAMS_WEBHOOK) {
            $webhookUrl = trim((string) ($data['teams_webhook_url'] ?? ''));

            if ($webhookUrl === '' && $record instanceof AlertDestination) {
                $webhookUrl = trim((string) Arr::get($existingConfig, 'webhook_url', ''));
            }

            $data['config'] = [
                'webhook_url' => $webhookUrl,
            ];
        }

        if ($type === AlertDestination::TYPE_EMAIL) {
            $recipients = Arr::wrap($data['email_recipients'] ?? []);
            $recipients = array_values(array_filter(array_map(static fn (mixed $value): string => trim((string) $value), $recipients)));

            if ($recipients === [] && $record instanceof AlertDestination) {
                $existingRecipients = Arr::get($existingConfig, 'recipients', []);
                $recipients = is_array($existingRecipients) ? array_values(array_filter(array_map(static fn (mixed $value): string => trim((string) $value), $existingRecipients))) : [];
            }

            $data['config'] = [
                'recipients' => array_values(array_unique($recipients)),
            ];
        }

        unset($data['teams_webhook_url'], $data['email_recipients']);

        return $data;
    }

    /**
     * @return array<string, string>
     */
    public static function typeOptions(): array
    {
        return [
            AlertDestination::TYPE_TEAMS_WEBHOOK => 'Microsoft Teams webhook',
            AlertDestination::TYPE_EMAIL => 'Email',
        ];
    }

    public static function typeLabel(string $type): string
    {
        return self::typeOptions()[$type] ?? ucfirst($type);
    }

    /**
     * @param  array<string, mixed>  $data
     */
    public static function assertValidConfigPayload(array $data): void
    {
        $type = (string) ($data['type'] ?? '');
        $config = is_array($data['config'] ?? null) ? $data['config'] : [];

        if ($type === AlertDestination::TYPE_TEAMS_WEBHOOK) {
            $webhook = trim((string) Arr::get($config, 'webhook_url', ''));

            if ($webhook === '') {
                throw ValidationException::withMessages([
                    'teams_webhook_url' => ['The Teams webhook URL is required.'],
                ]);
            }
        }

        if ($type === AlertDestination::TYPE_EMAIL) {
            $recipients = Arr::get($config, 'recipients', []);
            $recipients = is_array($recipients) ? array_values(array_filter(array_map(static fn (mixed $value): string => trim((string) $value), $recipients))) : [];

            if ($recipients === []) {
                throw ValidationException::withMessages([
                    'email_recipients' => ['At least one recipient is required for email destinations.'],
                ]);
            }
        }
    }

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

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

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