<?php

namespace App\Filament\Widgets;

use App\Filament\Resources\EventResource;
use App\Models\Event;
use App\Models\EventJoinToken;
use Filament\Tables;
use Filament\Widgets\Concerns\InteractsWithPageFilters;
use Filament\Widgets\TableWidget as BaseWidget;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;

class JoinTokenTopTokensWidget extends BaseWidget
{
    use InteractsWithPageFilters;

    protected static ?int $sort = 3;

    protected int|string|array $columnSpan = 'full';

    protected static ?string $heading = null;

    public function getHeading(): ?string
    {
        return __('admin.join_token_analytics.table.heading');
    }

    public function table(Tables\Table $table): Tables\Table
    {
        $filters = $this->resolveFilters();

        return $table
            ->query(fn (): Builder => $this->buildQuery($filters))
            ->columns([
                Tables\Columns\TextColumn::make('token_preview')
                    ->label(__('admin.join_token_analytics.table.token'))
                    ->copyable()
                    ->copyMessage(__('admin.events.messages.join_link_copied')),
                Tables\Columns\TextColumn::make('event_label')
                    ->label(__('admin.join_token_analytics.table.event'))
                    ->getStateUsing(fn (EventJoinToken $record) => $this->formatEventLabel($record->event))
                    ->url(fn (EventJoinToken $record) => $record->event
                        ? EventResource::getUrl('view', ['record' => $record->event])
                        : null)
                    ->openUrlInNewTab(),
                Tables\Columns\TextColumn::make('tenant_label')
                    ->label(__('admin.join_token_analytics.table.tenant'))
                    ->getStateUsing(fn (EventJoinToken $record) => $record->event?->tenant?->name ?? __('admin.common.unnamed')),
                Tables\Columns\TextColumn::make('success_total')
                    ->label(__('admin.join_token_analytics.table.success'))
                    ->numeric(),
                Tables\Columns\TextColumn::make('failure_total')
                    ->label(__('admin.join_token_analytics.table.failures'))
                    ->numeric(),
                Tables\Columns\TextColumn::make('rate_limited_total')
                    ->label(__('admin.join_token_analytics.table.rate_limited'))
                    ->numeric(),
                Tables\Columns\TextColumn::make('upload_total')
                    ->label(__('admin.join_token_analytics.table.uploads'))
                    ->numeric(),
                Tables\Columns\TextColumn::make('last_seen_at')
                    ->label(__('admin.join_token_analytics.table.last_seen'))
                    ->since()
                    ->placeholder('—'),
            ])
            ->paginated(false);
    }

    private function buildQuery(array $filters): Builder
    {
        $query = EventJoinToken::query()
            ->with(['event.tenant'])
            ->when($filters['event_id'], fn (Builder $builder, int $eventId) => $builder->where('event_id', $eventId))
            ->withCount([
                'analytics as success_total' => function (Builder $builder) use ($filters) {
                    $this->applyAnalyticsFilters($builder, $filters)
                        ->whereIn('event_type', self::SUCCESS_EVENTS);
                },
                'analytics as failure_total' => function (Builder $builder) use ($filters) {
                    $this->applyAnalyticsFilters($builder, $filters)
                        ->whereIn('event_type', self::FAILURE_EVENTS);
                },
                'analytics as rate_limited_total' => function (Builder $builder) use ($filters) {
                    $this->applyAnalyticsFilters($builder, $filters)
                        ->whereIn('event_type', self::RATE_LIMIT_EVENTS);
                },
                'analytics as upload_total' => function (Builder $builder) use ($filters) {
                    $this->applyAnalyticsFilters($builder, $filters)
                        ->whereIn('event_type', self::UPLOAD_EVENTS);
                },
            ])
            ->withMax([
                'analytics as last_seen_at' => function (Builder $builder) use ($filters) {
                    $this->applyAnalyticsFilters($builder, $filters);
                },
            ], 'occurred_at')
            ->orderByDesc('failure_total')
            ->orderByDesc('rate_limited_total')
            ->orderByDesc('success_total')
            ->limit(10);

        return $query;
    }

    private function applyAnalyticsFilters(Builder $query, array $filters): Builder
    {
        return $query->whereBetween('occurred_at', [$filters['start'], $filters['end']]);
    }

    private function resolveFilters(): array
    {
        $eventId = $this->pageFilters['event_id'] ?? null;
        $eventId = is_numeric($eventId) ? (int) $eventId : null;
        $range = is_string($this->pageFilters['range'] ?? null) ? $this->pageFilters['range'] : '24h';

        [$start, $end] = $this->resolveWindow($range, $eventId);

        return [
            'event_id' => $eventId,
            'range' => $range,
            'start' => $start,
            'end' => $end,
        ];
    }

    private function resolveWindow(string $range, ?int $eventId): array
    {
        $now = now();
        $start = match ($range) {
            '2h' => $now->copy()->subHours(2),
            '6h' => $now->copy()->subHours(6),
            '12h' => $now->copy()->subHours(12),
            '7d' => $now->copy()->subDays(7),
            default => $now->copy()->subHours(24),
        };
        $end = $now;

        if ($range === 'event_day' && $eventId) {
            $eventDate = Event::query()->whereKey($eventId)->value('date');

            if ($eventDate) {
                $eventDay = Carbon::parse($eventDate);
                $start = $eventDay->copy()->startOfDay();
                $end = $eventDay->copy()->endOfDay();
            }
        }

        return [$start, $end];
    }

    private function formatEventLabel(?Event $event): string
    {
        if (! $event) {
            return __('admin.common.unnamed');
        }

        $locale = app()->getLocale();
        $name = $event->name[$locale] ?? $event->name['de'] ?? $event->name['en'] ?? $event->slug ?? __('admin.common.unnamed');
        $tenant = $event->tenant?->name ?? __('admin.common.unnamed');
        $date = $event->date?->format('Y-m-d');

        return $date ? "{$name} ({$tenant}) {$date}" : "{$name} ({$tenant})";
    }

    private const SUCCESS_EVENTS = [
        'access_granted',
        'gallery_access_granted',
    ];

    private const RATE_LIMIT_EVENTS = [
        'token_rate_limited',
        'access_rate_limited',
        'download_rate_limited',
    ];

    private const FAILURE_EVENTS = [
        'invalid_token',
        'token_expired',
        'token_revoked',
        'event_not_public',
        'gallery_expired',
        'token_rate_limited',
        'access_rate_limited',
        'download_rate_limited',
    ];

    private const UPLOAD_EVENTS = [
        'upload_completed',
    ];
}
