<?php

namespace App\Services\Monitoring;

use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class PackageLimitMetrics
{
    private const CACHE_PREFIX = 'metrics:package_limits';

    private const INDEX_KEY = 'metrics:package_limits:index';

    private const TTL_MINUTES = 360;

    public static function recordGalleryWarning(int $daysRemaining): void
    {
        $label = sprintf('warning_day_%d', $daysRemaining);
        self::increment('gallery', $label, [
            'segment' => 'warning',
            'days_remaining' => $daysRemaining,
        ]);
    }

    public static function recordGalleryExpired(): void
    {
        self::increment('gallery', 'expired', ['segment' => 'expired']);
    }

    public static function recordTenantPackageWarning(int $daysRemaining): void
    {
        $label = sprintf('warning_day_%d', $daysRemaining);
        self::increment('tenant_package', $label, [
            'segment' => 'warning',
            'days_remaining' => $daysRemaining,
        ]);
    }

    public static function recordTenantPackageExpired(): void
    {
        self::increment('tenant_package', 'expired', ['segment' => 'expired']);
    }

    public static function recordCreditWarning(int $threshold, int $balance): void
    {
        $label = sprintf('threshold_%d', $threshold);
        self::increment('tenant_credit', $label, [
            'segment' => 'warning',
            'threshold' => $threshold,
            'balance' => $balance,
        ]);
    }

    public static function recordCreditRecovery(int $balance): void
    {
        self::increment('tenant_credit', 'recovered', [
            'segment' => 'recovered',
            'balance' => $balance,
        ]);
    }

    public static function snapshot(): array
    {
        $index = Cache::get(self::INDEX_KEY, []);
        $result = [];

        foreach ($index as $cacheKey => $meta) {
            $metric = Arr::get($meta, 'metric', 'unknown');
            $label = Arr::get($meta, 'label', 'unknown');
            $value = (int) Cache::get($cacheKey, 0);

            if (! isset($result[$metric])) {
                $result[$metric] = [];
            }

            $result[$metric][$label] = $value;
        }

        ksort($result);
        foreach ($result as &$labels) {
            ksort($labels);
        }

        return $result;
    }

    public static function reset(): void
    {
        $index = Cache::pull(self::INDEX_KEY, []);
        foreach (array_keys($index) as $cacheKey) {
            Cache::forget($cacheKey);
        }
    }

    private static function increment(string $metric, string $label, array $context = []): void
    {
        $cacheKey = self::buildCacheKey($metric, $label);

        if (! Cache::has($cacheKey)) {
            Cache::put($cacheKey, 0, now()->addMinutes(self::TTL_MINUTES));
        }

        Cache::increment($cacheKey);
        Cache::put($cacheKey, Cache::get($cacheKey), now()->addMinutes(self::TTL_MINUTES));

        self::rememberIndex($cacheKey, $metric, $label);

        Log::info('package_limit_metric', array_merge([
            'metric' => $metric,
            'label' => $label,
            'value' => Cache::get($cacheKey, 0),
        ], $context));
    }

    private static function buildCacheKey(string $metric, string $label): string
    {
        return sprintf('%s:%s:%s', self::CACHE_PREFIX, $metric, $label);
    }

    private static function rememberIndex(string $cacheKey, string $metric, string $label): void
    {
        $index = Cache::get(self::INDEX_KEY, []);
        $index[$cacheKey] = [
            'metric' => $metric,
            'label' => $label,
        ];

        Cache::put(self::INDEX_KEY, $index, now()->addMinutes(self::TTL_MINUTES));
    }
}
