<?php

namespace App\Services;

use App\Models\MexcoEventUsage;
use App\Models\MeetupEvent;
use Illuminate\Validation\ValidationException;

class UsageService
{
    public function recalculate(string $eventId): MexcoEventUsage
    {
        $event = MeetupEvent::query()
            ->withCount(['participants', 'sessions', 'booths'])
            ->findOrFail($eventId);

        $usage = MexcoEventUsage::query()->firstOrNew(['event_id' => $eventId]);
        $usage->participants_count = (int) $event->participants_count;
        $usage->sessions_count = (int) $event->sessions_count;
        $usage->booths_count = (int) $event->booths_count;
        $usage->last_calculated_at = now();
        $usage->save();

        return $usage;
    }

    public function ensure(string $eventId): MexcoEventUsage
    {
        $usage = MexcoEventUsage::query()->where('event_id', $eventId)->first();
        if ($usage) {
            return $usage;
        }

        return $this->recalculate($eventId);
    }

    public function currentCount(string $eventId, string $metric): int
    {
        $usage = $this->ensure($eventId);

        return match ($metric) {
            'participants' => (int) $usage->participants_count,
            'sessions' => (int) $usage->sessions_count,
            'booths' => (int) $usage->booths_count,
            'storage_mb' => (int) $usage->storage_used_mb,
            default => 0,
        };
    }

    public function usagePercent(string $eventId, string $metric, ?int $limit = null): ?float
    {
        $limit ??= app(EntitlementService::class)->limit($eventId, $metric);

        if ($limit === null || $limit <= 0) {
            return null;
        }

        $current = $this->currentCount($eventId, $metric);

        return min(100, round(($current / $limit) * 100, 1));
    }

    public function warningLevel(string $eventId, string $metric): ?string
    {
        $percent = $this->usagePercent($eventId, $metric);
        if ($percent === null) {
            return null;
        }
        if ($percent >= 100) {
            return 'limit100';
        }
        if ($percent >= 90) {
            return 'warn90';
        }
        if ($percent >= 80) {
            return 'warn80';
        }

        return null;
    }

    public function isOverLimit(string $eventId, string $metric): bool
    {
        return $this->warningLevel($eventId, $metric) === 'limit100';
    }

    public function assertCanAdd(string $eventId, string $metric, int $adding = 1): void
    {
        $limit = app(EntitlementService::class)->limit($eventId, $metric);
        if ($limit === null) {
            return;
        }

        $current = $this->currentCount($eventId, $metric);
        if ($current + $adding > $limit) {
            $label = match ($metric) {
                'participants' => 'participant',
                'sessions' => 'session',
                'booths' => 'booth',
                default => $metric,
            };

            throw ValidationException::withMessages([
                $metric => sprintf(
                    'This event has reached its %s limit (%d / %d). Upgrade the plan or remove items to continue.',
                    $label,
                    $current,
                    $limit
                ),
            ]);
        }
    }

    public function summary(string $eventId): array
    {
        $entitlements = app(EntitlementService::class);
        $metrics = ['participants', 'sessions', 'booths'];
        $out = [];

        foreach ($metrics as $metric) {
            $limit = $entitlements->limit($eventId, $metric);
            $current = $this->currentCount($eventId, $metric);
            $percent = $this->usagePercent($eventId, $metric, $limit);

            $out[$metric] = [
                'current' => $current,
                'limit' => $limit,
                'percent' => $percent,
                'level' => $this->warningLevel($eventId, $metric),
            ];
        }

        return $out;
    }
}
