<?php

namespace App\Filament\Widgets;

use App\Models\Event;
use App\Models\EventJoinTokenEvent;
use Filament\Widgets\Concerns\InteractsWithPageFilters;
use Filament\Widgets\StatsOverviewWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;

class JoinTokenOverviewWidget extends StatsOverviewWidget
{
    use InteractsWithPageFilters;

    protected static ?int $sort = 1;

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

    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',
    ];

    protected function getStats(): array
    {
        $filters = $this->resolveFilters();
        $totals = $this->totalsByEventType($filters);

        $success = $this->sumTotals($totals, self::SUCCESS_EVENTS);
        $failures = $this->sumTotals($totals, self::FAILURE_EVENTS);
        $rateLimited = $this->sumTotals($totals, self::RATE_LIMIT_EVENTS);
        $uploads = $this->sumTotals($totals, self::UPLOAD_EVENTS);

        $ratio = $success > 0 ? round(($failures / $success) * 100, 1) : null;
        $ratioLabel = $ratio !== null ? "{$ratio}%" : __('admin.join_token_analytics.stats.no_data');

        return [
            Stat::make(__('admin.join_token_analytics.stats.success'), number_format($success))
                ->color('success'),
            Stat::make(__('admin.join_token_analytics.stats.failures'), number_format($failures))
                ->color($failures > 0 ? 'danger' : 'success'),
            Stat::make(__('admin.join_token_analytics.stats.rate_limited'), number_format($rateLimited))
                ->color($rateLimited > 0 ? 'warning' : 'success'),
            Stat::make(__('admin.join_token_analytics.stats.uploads'), number_format($uploads))
                ->color('primary'),
            Stat::make(__('admin.join_token_analytics.stats.failure_ratio'), $ratioLabel)
                ->color($ratio !== null && $ratio >= 50 ? 'danger' : 'warning'),
        ];
    }

    private function totalsByEventType(array $filters): array
    {
        return $this->baseQuery($filters)
            ->selectRaw('event_type, COUNT(*) as total')
            ->groupBy('event_type')
            ->get()
            ->mapWithKeys(fn (EventJoinTokenEvent $event) => [$event->event_type => (int) $event->total])
            ->all();
    }

    private function sumTotals(array $totals, array $types): int
    {
        $sum = 0;

        foreach ($types as $type) {
            $sum += (int) ($totals[$type] ?? 0);
        }

        return $sum;
    }

    private function baseQuery(array $filters): Builder
    {
        $query = EventJoinTokenEvent::query()
            ->whereBetween('occurred_at', [$filters['start'], $filters['end']]);

        if ($filters['event_id']) {
            $query->where('event_id', $filters['event_id']);
        }

        return $query;
    }

    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];
    }
}
