<?php

namespace App\Filament\Widgets;

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

class JoinTokenTrendWidget extends ChartWidget
{
    use InteractsWithPageFilters;

    protected static ?int $sort = 2;

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

    protected function getData(): array
    {
        $filters = $this->resolveFilters();
        $events = $this->baseQuery($filters)->get(['event_type', 'occurred_at']);
        $hourly = $filters['start']->diffInHours($filters['end']) <= 48;
        $bucketFormat = $hourly ? 'Y-m-d H:00' : 'Y-m-d';
        $labelFormat = $hourly ? 'M d H:00' : 'M d';

        $periodStart = $hourly ? $filters['start']->copy()->startOfHour() : $filters['start']->copy()->startOfDay();
        $period = CarbonPeriod::create($periodStart, $hourly ? '1 hour' : '1 day', $filters['end']);

        $grouped = $events->groupBy(fn (EventJoinTokenEvent $event) => $event->occurred_at?->format($bucketFormat));

        $labels = [];
        $success = [];
        $failures = [];
        $rateLimited = [];
        $uploads = [];

        foreach ($period as $point) {
            $key = $point->format($bucketFormat);
            $bucket = $grouped->get($key, collect());

            $labels[] = $point->translatedFormat($labelFormat);
            $success[] = $bucket->whereIn('event_type', self::SUCCESS_EVENTS)->count();
            $failures[] = $bucket->whereIn('event_type', self::FAILURE_EVENTS)->count();
            $rateLimited[] = $bucket->whereIn('event_type', self::RATE_LIMIT_EVENTS)->count();
            $uploads[] = $bucket->whereIn('event_type', self::UPLOAD_EVENTS)->count();
        }

        return [
            'datasets' => [
                [
                    'label' => __('admin.join_token_analytics.trend.success'),
                    'data' => $success,
                    'borderColor' => '#16a34a',
                    'backgroundColor' => 'rgba(22, 163, 74, 0.2)',
                    'tension' => 0.35,
                    'fill' => false,
                ],
                [
                    'label' => __('admin.join_token_analytics.trend.failures'),
                    'data' => $failures,
                    'borderColor' => '#dc2626',
                    'backgroundColor' => 'rgba(220, 38, 38, 0.2)',
                    'tension' => 0.35,
                    'fill' => false,
                ],
                [
                    'label' => __('admin.join_token_analytics.trend.rate_limited'),
                    'data' => $rateLimited,
                    'borderColor' => '#f59e0b',
                    'backgroundColor' => 'rgba(245, 158, 11, 0.2)',
                    'tension' => 0.35,
                    'fill' => false,
                ],
                [
                    'label' => __('admin.join_token_analytics.trend.uploads'),
                    'data' => $uploads,
                    'borderColor' => '#2563eb',
                    'backgroundColor' => 'rgba(37, 99, 235, 0.2)',
                    'tension' => 0.35,
                    'fill' => false,
                ],
            ],
            'labels' => $labels,
        ];
    }

    protected function getType(): string
    {
        return 'line';
    }

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

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

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