<?php

namespace App\Services;

use App\Models\Park;
use App\Models\ParkArea;
use App\Models\Operator;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class StatisticsCacheService
{
    /**
     * Cache TTL in seconds (24 hours).
     */
    protected const CACHE_TTL = 86400;

    /**
     * Cache key prefixes.
     */
    protected const AREA_PREFIX = 'stats:area:';
    protected const PARK_PREFIX = 'stats:park:';
    protected const OPERATOR_PREFIX = 'stats:operator:';

    /**
     * Cache area statistics.
     */
    public function cacheAreaStatistics(ParkArea $area): void
    {
        try {
            Cache::put(
                $this->getAreaKey($area),
                [
                    'ride_distribution' => $area->ride_distribution,
                    'daily_capacity' => $area->formatted_daily_capacity,
                    'rating' => $area->rating_display,
                    'wait_time' => $area->formatted_peak_wait_time,
                    'historical' => $area->historical_stats,
                    'updated_at' => now(),
                ],
                static::CACHE_TTL
            );

            Log::info("Cached statistics for area {$area->id}");
        } catch (\Exception $e) {
            Log::error("Failed to cache area statistics: {$e->getMessage()}");
        }
    }

    /**
     * Cache park statistics.
     */
    public function cacheParkStatistics(Park $park): void
    {
        try {
            Cache::put(
                $this->getParkKey($park),
                [
                    'area_distribution' => $park->area_distribution,
                    'ride_distribution' => $park->ride_distribution,
                    'daily_capacity' => $park->formatted_daily_capacity,
                    'rating' => $park->rating_display,
                    'wait_time' => $park->formatted_wait_time,
                    'historical' => $park->historical_stats,
                    'performance' => $park->performance_metrics,
                    'updated_at' => now(),
                ],
                static::CACHE_TTL
            );

            Log::info("Cached statistics for park {$park->id}");
        } catch (\Exception $e) {
            Log::error("Failed to cache park statistics: {$e->getMessage()}");
        }
    }

    /**
     * Cache operator statistics.
     */
    public function cacheOperatorStatistics(Operator $operator): void
    {
        try {
            Cache::put(
                $this->getOperatorKey($operator),
                [
                    'park_count' => $operator->total_parks,
                    'operating_parks' => $operator->operating_parks,
                    'closed_parks' => $operator->closed_parks,
                    'total_rides' => $operator->total_rides,
                    'total_coasters' => $operator->total_coasters,
                    'average_rating' => $operator->average_rating,
                    'total_capacity' => $operator->total_daily_capacity,
                    'updated_at' => now(),
                ],
                static::CACHE_TTL
            );

            Log::info("Cached statistics for operator {$operator->id}");
        } catch (\Exception $e) {
            Log::error("Failed to cache operator statistics: {$e->getMessage()}");
        }
    }

    /**
     * Get cached area statistics.
     *
     * @return array<string, mixed>|null
     */
    public function getAreaStatistics(ParkArea $area): ?array
    {
        return Cache::get($this->getAreaKey($area));
    }

    /**
     * Get cached park statistics.
     *
     * @return array<string, mixed>|null
     */
    public function getParkStatistics(Park $park): ?array
    {
        return Cache::get($this->getParkKey($park));
    }

    /**
     * Get cached operator statistics.
     *
     * @return array<string, mixed>|null
     */
    public function getOperatorStatistics(Operator $operator): ?array
    {
        return Cache::get($this->getOperatorKey($operator));
    }

    /**
     * Invalidate area statistics cache.
     */
    public function invalidateAreaCache(ParkArea $area): void
    {
        Cache::forget($this->getAreaKey($area));
        Log::info("Invalidated cache for area {$area->id}");
    }

    /**
     * Invalidate park statistics cache.
     */
    public function invalidateParkCache(Park $park): void
    {
        Cache::forget($this->getParkKey($park));
        Log::info("Invalidated cache for park {$park->id}");
    }

    /**
     * Invalidate operator statistics cache.
     */
    public function invalidateOperatorCache(Operator $operator): void
    {
        Cache::forget($this->getOperatorKey($operator));
        Log::info("Invalidated cache for operator {$operator->id}");
    }

    /**
     * Warm up caches for all entities.
     */
    public function warmCaches(): void
    {
        try {
            // Cache area statistics
            ParkArea::chunk(100, function ($areas) {
                foreach ($areas as $area) {
                    $this->cacheAreaStatistics($area);
                }
            });

            // Cache park statistics
            Park::chunk(100, function ($parks) {
                foreach ($parks as $park) {
                    $this->cacheParkStatistics($park);
                }
            });

            // Cache operator statistics
            Operator::chunk(100, function ($operators) {
                foreach ($operators as $operator) {
                    $this->cacheOperatorStatistics($operator);
                }
            });

            Log::info('Successfully warmed up all statistics caches');
        } catch (\Exception $e) {
            Log::error("Failed to warm up caches: {$e->getMessage()}");
        }
    }

    /**
     * Get cache key for area.
     */
    protected function getAreaKey(ParkArea $area): string
    {
        return static::AREA_PREFIX . $area->id;
    }

    /**
     * Get cache key for park.
     */
    protected function getParkKey(Park $park): string
    {
        return static::PARK_PREFIX . $park->id;
    }

    /**
     * Get cache key for operator.
     */
    protected function getOperatorKey(Operator $operator): string
    {
        return static::OPERATOR_PREFIX . $operator->id;
    }
}