<?php

declare(strict_types=1);

use App\Models\EnhanceJob;
use App\Models\User;
use App\Services\Enhance\Processors\ExternalWorkerEnhanceProcessor;
use Illuminate\Http\Client\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;

beforeEach(function (): void {
    config()->set('app.url', 'http://skinbase.test');
    config()->set('enhance.disk', 'public');
    config()->set('enhance.external_worker.url', 'http://127.0.0.1:8095');
    config()->set('enhance.external_worker.token', 'worker-secret');
    config()->set('enhance.external_worker.timeout', 15);
    config()->set('enhance.external_worker.max_download_mb', 2);
    Storage::fake('public');
});

function makeEnhanceJob(): EnhanceJob {
    $user = User::factory()->create();
    Storage::disk('public')->put('enhance/sources/10/source.png', UploadedFile::fake()->image('source.png', 40, 40)->get());

    return EnhanceJob::query()->create([
        'user_id' => $user->id,
        'status' => EnhanceJob::STATUS_QUEUED,
        'engine' => EnhanceJob::ENGINE_EXTERNAL_WORKER,
        'mode' => 'artwork',
        'scale' => 2,
        'source_disk' => 'public',
        'source_path' => 'enhance/sources/10/source.png',
        'input_mime' => 'image/png',
    ]);
}

it('requires a configured worker url', function (): void {
    config()->set('enhance.external_worker.url', '');

    app(ExternalWorkerEnhanceProcessor::class)->process(makeEnhanceJob());
})->throws(RuntimeException::class, 'Worker URL is missing.');

it('requires a configured worker token', function (): void {
    config()->set('enhance.external_worker.token', '');

    app(ExternalWorkerEnhanceProcessor::class)->process(makeEnhanceJob());
})->throws(RuntimeException::class, 'Worker token is missing.');

it('sends bearer token and stores successful worker output', function (): void {
    $job = makeEnhanceJob();
    $outputBinary = UploadedFile::fake()->image('output.png', 80, 80)->get();

    Http::fake([
        'http://127.0.0.1:8095/v1/upscale' => Http::response([
            'success' => true,
            'job_id' => $job->id,
            'output_url' => 'http://127.0.0.1:8095/v1/results/result-output.png',
            'width' => 80,
            'height' => 80,
            'filesize' => strlen($outputBinary),
            'mime' => 'image/png',
            'metadata' => ['engine' => 'pillow'],
        ], 200),
        'http://127.0.0.1:8095/v1/results/result-output.png' => Http::response($outputBinary, 200, ['Content-Type' => 'image/png']),
        'http://127.0.0.1:8095/v1/results/result-output.png*' => Http::response(['success' => true, 'deleted' => true], 200),
    ]);

    $result = app(ExternalWorkerEnhanceProcessor::class)->process($job);

    expect($result->width)->toBe(80);
    expect($result->height)->toBe(80);
    expect($result->mime)->toBe('image/png');
    expect($result->metadata['engine'])->toBe('pillow');
    Storage::disk('public')->assertExists($result->path);

    Http::assertSent(function (Request $request): bool {
        if ($request->url() !== 'http://127.0.0.1:8095/v1/upscale') {
            return false;
        }

        $data = $request->data();
        $sourceUrl = (string) ($data['source_url'] ?? '');

        return $request->hasHeader('Authorization')
            && ($data['mode'] ?? null) === 'artwork'
            && (int) ($data['scale'] ?? 0) === 2
            && (int) ($data['job_id'] ?? 0) > 0
            && ($sourceUrl !== '')
            && (str_contains($sourceUrl, '/internal/enhance/source/') || str_contains($sourceUrl, '/enhance/sources/'));
    });
});

it('handles a failed worker response', function (): void {
    $job = makeEnhanceJob();

    Http::fake([
        'http://127.0.0.1:8095/v1/upscale' => Http::response(['success' => false, 'error' => 'Worker rejected the image.'], 422),
    ]);

    app(ExternalWorkerEnhanceProcessor::class)->process($job);
})->throws(RuntimeException::class, 'Worker rejected the image.');

it('handles an invalid json worker response', function (): void {
    $job = makeEnhanceJob();

    Http::fake([
        'http://127.0.0.1:8095/v1/upscale' => Http::response('not-json', 200, ['Content-Type' => 'text/plain']),
    ]);

    app(ExternalWorkerEnhanceProcessor::class)->process($job);
})->throws(RuntimeException::class, 'Worker returned an invalid response.');

it('rejects oversized downloaded output', function (): void {
    $job = makeEnhanceJob();
    config()->set('enhance.external_worker.max_download_mb', 1);

    Http::fake([
        'http://127.0.0.1:8095/v1/upscale' => Http::response([
            'success' => true,
            'job_id' => $job->id,
            'output_url' => 'http://127.0.0.1:8095/v1/results/too-large.webp',
            'mime' => 'image/webp',
        ], 200),
        'http://127.0.0.1:8095/v1/results/too-large.webp' => Http::response(str_repeat('a', (1024 * 1024) + 1), 200, ['Content-Type' => 'image/webp']),
    ]);

    app(ExternalWorkerEnhanceProcessor::class)->process($job);
})->throws(RuntimeException::class, 'The upscaled output exceeded the maximum allowed size.');

it('accepts base64 worker output', function (): void {
    $job = makeEnhanceJob();
    $outputBinary = UploadedFile::fake()->image('output.png', 90, 60)->get();

    Http::fake([
        'http://127.0.0.1:8095/v1/upscale' => Http::response([
            'success' => true,
            'job_id' => $job->id,
            'output_base64' => base64_encode($outputBinary),
            'mime' => 'image/png',
            'metadata' => ['engine' => 'pillow', 'real_ai_upscale' => false],
        ], 200),
    ]);

    $result = app(ExternalWorkerEnhanceProcessor::class)->process($job);

    expect($result->width)->toBe(90);
    expect($result->height)->toBe(60);
    expect($result->metadata['real_ai_upscale'])->toBeFalse();
});