<?php

declare(strict_types=1);

namespace App\Services\Enhance;

use Illuminate\Http\UploadedFile;
use Illuminate\Validation\ValidationException;

final class EnhanceValidator
{
    public function validateUpload(UploadedFile $file, array $options): array
    {
        $path = (string) ($file->getRealPath() ?: $file->getPathname());

        if ($path === '' || ! is_readable($path)) {
            throw ValidationException::withMessages([
                'image' => 'Unable to read the uploaded image.',
            ]);
        }

        $binary = file_get_contents($path);

        if (! is_string($binary) || $binary === '') {
            throw ValidationException::withMessages([
                'image' => 'Unable to read the uploaded image.',
            ]);
        }

        return $this->validateBinary($binary, $options, (int) ($file->getSize() ?? strlen($binary)));
    }

    public function validateBinary(string $binary, array $options, ?int $filesize = null): array
    {
        $normalized = $this->normalizeOptions($options);
        $size = $filesize ?? strlen($binary);

        if ($size <= 0) {
            throw ValidationException::withMessages([
                'image' => 'Uploaded image is empty.',
            ]);
        }

        $maxBytes = (int) config('enhance.max_upload_mb', 20) * 1024 * 1024;
        if ($maxBytes > 0 && $size > $maxBytes) {
            throw ValidationException::withMessages([
                'image' => sprintf('The image may not be greater than %d MB.', (int) config('enhance.max_upload_mb', 20)),
            ]);
        }

        $finfo = new \finfo(FILEINFO_MIME_TYPE);
        $mime = strtolower((string) $finfo->buffer($binary));

        if (! in_array($mime, (array) config('enhance.allowed_mimes', []), true)) {
            throw ValidationException::withMessages([
                'image' => 'Unsupported image format. Upload a JPEG, PNG, or WebP image.',
            ]);
        }

        $dimensions = @getimagesizefromstring($binary);

        if (! is_array($dimensions) || (int) ($dimensions[0] ?? 0) < 1 || (int) ($dimensions[1] ?? 0) < 1) {
            throw ValidationException::withMessages([
                'image' => 'Uploaded file is not a valid image.',
            ]);
        }

        $width = (int) ($dimensions[0] ?? 0);
        $height = (int) ($dimensions[1] ?? 0);

        if ($width > (int) config('enhance.max_input_width', 4096)) {
            throw ValidationException::withMessages([
                'image' => sprintf('Image width may not exceed %d pixels.', (int) config('enhance.max_input_width', 4096)),
            ]);
        }

        if ($height > (int) config('enhance.max_input_height', 4096)) {
            throw ValidationException::withMessages([
                'image' => sprintf('Image height may not exceed %d pixels.', (int) config('enhance.max_input_height', 4096)),
            ]);
        }

        return $normalized + [
            'input_width' => $width,
            'input_height' => $height,
            'input_filesize' => $size,
            'input_mime' => $mime,
        ];
    }

    public function normalizeOptions(array $options): array
    {
        $allowedScales = array_map('intval', (array) config('enhance.allowed_scales', [2, 4]));
        $allowedModes = array_map('strval', (array) config('enhance.allowed_modes', ['standard', 'artwork', 'photo', 'illustration']));
        $allowedEngines = [
            \App\Models\EnhanceJob::ENGINE_STUB,
            \App\Models\EnhanceJob::ENGINE_EXTERNAL_WORKER,
        ];

        $scale = (int) ($options['scale'] ?? config('enhance.allowed_scales.0', 2));
        $mode = trim((string) ($options['mode'] ?? 'standard'));
        $engine = trim((string) ($options['engine'] ?? config('enhance.default_engine', \App\Models\EnhanceJob::ENGINE_STUB)));

        if (! in_array($scale, $allowedScales, true)) {
            throw ValidationException::withMessages([
                'scale' => 'Please select a supported scale.',
            ]);
        }

        if (! in_array($mode, $allowedModes, true)) {
            throw ValidationException::withMessages([
                'mode' => 'Please select a supported enhance mode.',
            ]);
        }

        if (! in_array($engine, $allowedEngines, true)) {
            throw ValidationException::withMessages([
                'engine' => 'Please select a supported enhance engine.',
            ]);
        }

        return [
            'scale' => $scale,
            'mode' => $mode,
            'engine' => $engine,
        ];
    }
}