<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Spatie\MediaLibrary\MediaCollections\Models\Media;

class CleanupOrphanedMediaJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 3;

    public int $chunkSize = 100;

    public function handle(): void
    {
        $orphanedMedia = $this->getOrphanedMedia();

        if ($orphanedMedia->isEmpty()) {
            Log::info('CleanupOrphanedMediaJob: Tidak ada media orphaned.');

            return;
        }

        $totalDeleted = 0;

        $orphanedMedia->each(function (Media $media) use (&$totalDeleted) {
            $this->deleteMedia($media);
            $totalDeleted++;
        });

        Log::info("CleanupOrphanedMediaJob: Berhasil hapus {$totalDeleted} media orphaned.");
    }

    private function getOrphanedMedia()
    {
        $allMedia = Media::select(['id', 'model_type', 'model_id', 'file_name', 'custom_properties', 'disk'])
            ->get()
            ->groupBy(fn (Media $m) => $m->model_type.'|'.$m->model_id);

        $orphanedIds = [];

        foreach ($allMedia as $key => $mediaItems) {
            [$modelType, $modelId] = explode('|', $key);

            if ($this->isOrphaned($modelType, (int) $modelId)) {
                foreach ($mediaItems as $media) {
                    $orphanedIds[] = $media->id;
                }
            }
        }

        if (empty($orphanedIds)) {
            return collect();
        }

        return Media::whereIn('id', $orphanedIds)->get();
    }

    private function isOrphaned(string $modelType, int $modelId): bool
    {
        if (! class_exists($modelType)) {
            return true;
        }

        $model = $modelType::withTrashed()->find($modelId);

        if (! $model) {
            return true;
        }

        if (method_exists($model, 'trashed') && $model->trashed()) {
            return $model->deleted_at->lt(now()->subMonth());
        }

        return false;
    }

    private function deleteMedia(Media $media): void
    {
        $s3Key = $media->getCustomProperty('s3_key');

        if ($s3Key && Storage::disk($media->disk)->exists($s3Key)) {
            Storage::disk($media->disk)->delete($s3Key);
        }

        $media->forceDelete();
    }
}
