<?php

declare(strict_types=1);

use App\Models\EnhanceJob;
use App\Models\User;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

beforeEach(function (): void {
    config()->set('enhance.disk', 'public');
    config()->set('enhance.lifecycle.cleanup_chunk_size', 10);
    Storage::fake('public');
});

it('does not delete files during cleanup dry run', function (): void {
    $owner = User::factory()->create();
    Storage::disk('public')->put('enhance/sources/1/source.png', UploadedFile::fake()->image('source.png')->get());
    Storage::disk('public')->put('enhance/outputs/1/output.webp', UploadedFile::fake()->image('output.webp')->get());
    Storage::disk('public')->put('enhance/previews/1/preview.webp', UploadedFile::fake()->image('preview.webp')->get());

    $job = EnhanceJob::query()->create([
        'user_id' => $owner->id,
        'status' => EnhanceJob::STATUS_COMPLETED,
        'engine' => EnhanceJob::ENGINE_STUB,
        'mode' => 'standard',
        'scale' => 2,
        'source_disk' => 'public',
        'source_path' => 'enhance/sources/1/source.png',
        'output_disk' => 'public',
        'output_path' => 'enhance/outputs/1/output.webp',
        'preview_disk' => 'public',
        'preview_path' => 'enhance/previews/1/preview.webp',
        'expires_at' => now()->subMinute(),
    ]);

    $this->artisan('enhance:cleanup --dry-run')->assertSuccessful();

    $job->refresh();

    expect($job->status)->toBe(EnhanceJob::STATUS_COMPLETED);
    Storage::disk('public')->assertExists('enhance/sources/1/source.png');
    Storage::disk('public')->assertExists('enhance/outputs/1/output.webp');
    Storage::disk('public')->assertExists('enhance/previews/1/preview.webp');
});

it('deletes expired completed job files in force mode', function (): void {
    $owner = User::factory()->create();
    Storage::disk('public')->put('enhance/sources/2/source.png', UploadedFile::fake()->image('source.png')->get());
    Storage::disk('public')->put('enhance/outputs/2/output.webp', UploadedFile::fake()->image('output.webp')->get());
    Storage::disk('public')->put('enhance/previews/2/preview.webp', UploadedFile::fake()->image('preview.webp')->get());

    $job = EnhanceJob::query()->create([
        'user_id' => $owner->id,
        'status' => EnhanceJob::STATUS_COMPLETED,
        'engine' => EnhanceJob::ENGINE_STUB,
        'mode' => 'standard',
        'scale' => 2,
        'source_disk' => 'public',
        'source_path' => 'enhance/sources/2/source.png',
        'output_disk' => 'public',
        'output_path' => 'enhance/outputs/2/output.webp',
        'preview_disk' => 'public',
        'preview_path' => 'enhance/previews/2/preview.webp',
        'expires_at' => now()->subMinute(),
    ]);

    $this->artisan('enhance:cleanup --only=expired --force')->assertSuccessful();

    $job->refresh();

    expect($job->status)->toBe(EnhanceJob::STATUS_EXPIRED);
    expect($job->source_path)->toBeNull();
    expect($job->output_path)->toBeNull();
    expect($job->preview_path)->toBeNull();
    expect($job->metadata['cleanup']['reason'])->toBe('expired');
    Storage::disk('public')->assertMissing('enhance/sources/2/source.png');
    Storage::disk('public')->assertMissing('enhance/outputs/2/output.webp');
    Storage::disk('public')->assertMissing('enhance/previews/2/preview.webp');
});

it('does not delete non enhance paths during cleanup', function (): void {
    $owner = User::factory()->create();
    Storage::disk('public')->put('uploads/artworks/unsafe.png', 'unsafe');
    Storage::disk('public')->put('enhance/outputs/3/output.webp', UploadedFile::fake()->image('output.webp')->get());

    $job = EnhanceJob::query()->create([
        'user_id' => $owner->id,
        'status' => EnhanceJob::STATUS_FAILED,
        'engine' => EnhanceJob::ENGINE_STUB,
        'mode' => 'standard',
        'scale' => 2,
        'source_disk' => 'public',
        'source_path' => 'uploads/artworks/unsafe.png',
        'output_disk' => 'public',
        'output_path' => 'enhance/outputs/3/output.webp',
        'finished_at' => now()->subDays(10),
    ]);

    $this->artisan('enhance:cleanup --only=failed --days=3 --force')->assertSuccessful();

    $job->refresh();

    expect($job->source_path)->toBe('uploads/artworks/unsafe.png');
    expect($job->output_path)->toBeNull();
    Storage::disk('public')->assertExists('uploads/artworks/unsafe.png');
    Storage::disk('public')->assertMissing('enhance/outputs/3/output.webp');
});

it('cleans failed and soft deleted enhance files and records cleanup metadata', function (): void {
    $owner = User::factory()->create();

    Storage::disk('public')->put('enhance/sources/4/source.png', UploadedFile::fake()->image('source.png')->get());
    $failed = EnhanceJob::query()->create([
        'user_id' => $owner->id,
        'status' => EnhanceJob::STATUS_FAILED,
        'engine' => EnhanceJob::ENGINE_STUB,
        'mode' => 'standard',
        'scale' => 2,
        'source_disk' => 'public',
        'source_path' => 'enhance/sources/4/source.png',
        'finished_at' => now()->subDays(10),
    ]);

    Storage::disk('public')->put('enhance/sources/5/source.png', UploadedFile::fake()->image('source.png')->get());
    $deleted = EnhanceJob::query()->create([
        'user_id' => $owner->id,
        'status' => EnhanceJob::STATUS_COMPLETED,
        'engine' => EnhanceJob::ENGINE_STUB,
        'mode' => 'standard',
        'scale' => 2,
        'source_disk' => 'public',
        'source_path' => 'enhance/sources/5/source.png',
    ]);
    $deleted->delete();
    $deleted->forceFill(['deleted_at' => now()->subDays(5)])->saveQuietly();

    $this->artisan('enhance:cleanup --only=failed --days=3 --force')->assertSuccessful();
    $this->artisan('enhance:cleanup --only=deleted --days=3 --force')->assertSuccessful();

    $failed->refresh();
    $deleted = EnhanceJob::withTrashed()->findOrFail($deleted->id);

    expect($failed->metadata['cleanup']['reason'])->toBe('failed-expired');
    expect($deleted->metadata['cleanup']['reason'])->toBe('deleted-grace');
    Storage::disk('public')->assertMissing('enhance/sources/4/source.png');
    Storage::disk('public')->assertMissing('enhance/sources/5/source.png');
});