<?php

namespace App\Console\Commands;

use App\Console\Concerns\InteractsWithCacheLocks;
use App\Jobs\ArchiveEventMediaAssets;
use App\Models\Event;
use App\Services\Compliance\RetentionOverrideService;
use Illuminate\Console\Command;
use Illuminate\Contracts\Cache\Lock;
use Illuminate\Support\Facades\Log;

class DispatchStorageArchiveCommand extends Command
{
    use InteractsWithCacheLocks;

    protected $signature = 'storage:archive-pending
        {--event= : Limit processing to a specific event ID}
        {--force : Run even if another dispatcher instance is active}';

    protected $description = 'Queue archive jobs for events whose galleries expired or were manually archived.';

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

        if ($lock === false) {
            $this->warn('Another archive dispatcher run is already executing.');

            return self::SUCCESS;
        }

        $eventLockTtl = (int) config('storage-monitor.archive.event_lock_seconds', 3600);
        $graceDays = max(0, (int) config('storage-monitor.archive.grace_days', 3));
        $cutoff = now()->subDays($graceDays);
        $chunkSize = max(1, (int) config('storage-monitor.archive.chunk', 25));
        $maxDispatch = max(1, (int) config('storage-monitor.archive.max_dispatch', 100));
        $eventId = $this->option('event');
        $dispatched = 0;
        $overrides = app(RetentionOverrideService::class);

        try {
            $query = Event::query()
                ->with('eventPackages:id,event_id,gallery_expires_at')
                ->whereHas('mediaAssets', function ($builder) {
                    $builder->where('status', '!=', 'archived');
                });

            if ($eventId) {
                $query->whereKey($eventId);
            } else {
                $query->where(function ($builder) use ($cutoff) {
                    $builder->where('status', 'archived')
                        ->orWhereHas('eventPackages', function ($packages) use ($cutoff) {
                            $packages->whereNotNull('gallery_expires_at')
                                ->where('gallery_expires_at', '<=', $cutoff);
                        });
                });
            }

            $query->chunkById($chunkSize, function ($events) use (&$dispatched, $maxDispatch, $eventLockTtl, $overrides) {
                foreach ($events as $event) {
                    if ($dispatched >= $maxDispatch) {
                        return false;
                    }

                    if ($overrides->eventOnHold($event)) {
                        continue;
                    }

                    $eventLock = $this->acquireCommandLock('storage:archive-event-'.$event->id, $eventLockTtl);
                    if ($eventLock === false) {
                        Log::channel('storage-jobs')->info('Archive dispatch skipped due to in-flight lock', [
                            'event_id' => $event->id,
                        ]);

                        continue;
                    }

                    try {
                        ArchiveEventMediaAssets::dispatch($event->id);
                        $dispatched++;

                        Log::channel('storage-jobs')->info('Archive job dispatched', [
                            'event_id' => $event->id,
                            'queue' => 'media-storage',
                        ]);
                    } finally {
                        if ($eventLock instanceof Lock) {
                            $eventLock->release();
                        }
                    }
                }

                return null;
            });

            $this->info(sprintf('Dispatched %d archive job(s).', $dispatched));
            Log::channel('storage-jobs')->info('Archive dispatch run finished', [
                'dispatched' => $dispatched,
                'event_limit' => $eventId,
            ]);

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