<?php

namespace App\Console\Commands;

use App\Console\Concerns\InteractsWithCacheLocks;
use App\Models\EventMediaAsset;
use App\Models\MediaStorageTarget;
use App\Services\Storage\StorageHealthService;
use Illuminate\Console\Command;
use Illuminate\Contracts\Cache\Lock;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class MonitorStorageCommand extends Command
{
    use InteractsWithCacheLocks;

    protected $signature = 'storage:monitor {--force : Execute even if another run is still in progress}';

    protected $description = 'Collect storage capacity statistics and cache them for dashboards & alerts.';

    public function __construct(private readonly StorageHealthService $storageHealth)
    {
        parent::__construct();
    }

    public function handle(): int
    {
        $lockSeconds = (int) config('storage-monitor.monitor.lock_seconds', 300);
        $lock = $this->acquireCommandLock('storage:monitor', $lockSeconds, (bool) $this->option('force'));

        if ($lock === false) {
            $this->warn('storage:monitor is already running on another worker.');

            return self::SUCCESS;
        }

        try {
            $targets = MediaStorageTarget::active()->get();

            if ($targets->isEmpty()) {
                $this->info('No media storage targets available.');

                return self::SUCCESS;
            }

            $assetStats = $this->buildAssetStatistics();
            $thresholds = $this->capacityThresholds();
            $alerts = [];
            $snapshotTargets = [];

            foreach ($targets as $target) {
                $capacity = $this->storageHealth->getCapacity($target);
                $assets = $assetStats[$target->id] ?? [
                    'total' => 0,
                    'bytes' => 0,
                    'by_status' => [],
                ];

                $severity = $this->determineCapacitySeverity($capacity, $thresholds);
                if ($severity !== 'ok') {
                    $alerts[] = [
                        'target' => $target->key,
                        'type' => 'capacity',
                        'severity' => $severity,
                        'percentage' => $capacity['percentage'] ?? null,
                        'status' => $capacity['status'] ?? null,
                    ];
                }

                $failedCount = $assets['by_status']['failed']['count'] ?? 0;
                if ($failedCount > 0) {
                    $alerts[] = [
                        'target' => $target->key,
                        'type' => 'failed_assets',
                        'severity' => 'warning',
                        'failed' => $failedCount,
                    ];
                }

                $snapshotTargets[] = [
                    'id' => $target->id,
                    'key' => $target->key,
                    'name' => $target->name,
                    'is_hot' => (bool) $target->is_hot,
                    'capacity' => $capacity,
                    'assets' => $assets,
                ];
            }

            $snapshot = [
                'generated_at' => now()->toIso8601String(),
                'targets' => $snapshotTargets,
                'alerts' => $alerts,
            ];

            $ttlMinutes = max(1, (int) config('storage-monitor.monitor.cache_minutes', 15));
            Cache::put('storage:monitor:last', $snapshot, now()->addMinutes($ttlMinutes));

            Log::channel('storage-jobs')->info('Storage monitor snapshot generated', [
                'targets' => count($snapshotTargets),
                'alerts' => count($alerts),
            ]);

            $this->info(sprintf(
                'Storage monitor finished: %d targets, %d alerts.',
                count($snapshotTargets),
                count($alerts)
            ));

            return self::SUCCESS;
        } finally {
            if ($lock instanceof Lock) {
                $lock->release();
            }
        }
    }

    /**
     * @return array<int, array>
     */
    private function buildAssetStatistics(): array
    {
        return EventMediaAsset::query()
            ->selectRaw('media_storage_target_id, status, COUNT(*) as total_count, COALESCE(SUM(size_bytes), 0) as total_bytes')
            ->groupBy('media_storage_target_id', 'status')
            ->get()
            ->groupBy('media_storage_target_id')
            ->map(function ($rows) {
                $byStatus = [];
                $totalCount = 0;
                $totalBytes = 0;

                foreach ($rows as $row) {
                    $count = (int) ($row->total_count ?? 0);
                    $bytes = (int) ($row->total_bytes ?? 0);

                    $totalCount += $count;
                    $totalBytes += $bytes;
                    $byStatus[$row->status] = [
                        'count' => $count,
                        'bytes' => $bytes,
                    ];
                }

                ksort($byStatus);

                return [
                    'total' => $totalCount,
                    'bytes' => $totalBytes,
                    'by_status' => $byStatus,
                ];
            })
            ->all();
    }

    private function capacityThresholds(): array
    {
        $warning = (int) config('storage-monitor.capacity_thresholds.warning', 75);
        $critical = (int) config('storage-monitor.capacity_thresholds.critical', 90);

        if ($warning > $critical) {
            [$warning, $critical] = [$critical, $warning];
        }

        return [
            'warning' => $warning,
            'critical' => $critical,
        ];
    }

    private function determineCapacitySeverity(array $capacity, array $thresholds): string
    {
        $status = $capacity['status'] ?? 'ok';
        if ($status !== 'ok') {
            return $status;
        }

        $percentage = $capacity['percentage'] ?? null;
        if ($percentage === null) {
            return 'unknown';
        }

        if ($percentage >= ($thresholds['critical'] ?? 95)) {
            return 'critical';
        }

        if ($percentage >= ($thresholds['warning'] ?? 75)) {
            return 'warning';
        }

        return 'ok';
    }
}
