<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\GraduateVideo;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;

class GraduateVideosController extends Controller
{
    private const ALLOWED_EXTENSIONS = ['mp4', 'webm', 'ogg', 'ogv', 'mov', 'avi', 'mkv'];

    private const ALLOWED_THUMB_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp'];

    /** @var array|null */
    private $resolvedUploadLimits;

    public function index()
    {
        $this->authorize('admin_additional_pages');

        $videos = GraduateVideo::query()
            ->orderBy('sort_order')
            ->orderByDesc('id')
            ->paginate(30);

        $data = [
            'pageTitle' => 'فيديوهات الخريجين',
            'videos' => $videos,
            'totalVideosCount' => GraduateVideo::query()->count(),
            'enabledVideosCount' => GraduateVideo::query()->where('enabled', true)->count(),
            'maxUploadMb' => round($this->effectiveMaxUploadKb() / 1024, 1),
            'phpUploadIni' => ini_get('upload_max_filesize'),
            'phpPostIni' => ini_get('post_max_size'),
        ];

        return view('admin.graduate_videos.index', $data);
    }

    public function create()
    {
        $this->authorize('admin_additional_pages');

        $data = [
            'pageTitle' => 'إضافة فيديو خريج',
            'maxUploadMb' => round($this->effectiveMaxUploadKb() / 1024, 1),
            'phpUploadIni' => ini_get('upload_max_filesize'),
            'phpPostIni' => ini_get('post_max_size'),
        ];

        return view('admin.graduate_videos.create', $data);
    }

    public function store(Request $request)
    {
        $this->authorize('admin_additional_pages');

        @set_time_limit(600);

        $this->logGraduateVideoUploadContext($request, 'store_begin');

        $maxKb = $this->effectiveMaxUploadKb();
        try {
            $request->validate(
                $this->graduateVideoValidationRules($maxKb, null),
                $this->graduateVideoFileValidationMessages($maxKb)
            );
        } catch (ValidationException $e) {
            $this->logGraduateVideoUploadContext($request, 'store_validation_failed', [
                'validation_errors' => $e->errors(),
            ]);
            Log::warning('GraduateVideoValidationFailed', [
                'action' => 'store',
                'errors' => $e->errors(),
                'upload_limits' => $this->resolveUploadLimits()['breakdown'],
            ]);

            throw $e;
        }

        $hasFile = $request->hasFile('video_file') && $request->file('video_file')->isValid();
        $hasUrl = strlen(trim((string) $request->input('video_url', ''))) > 0;

        $this->logGraduateVideoUploadContext($request, 'store_after_validation', [
            'has_file_valid' => $hasFile,
            'has_url' => $hasUrl,
        ]);

        if (! $hasFile && ! $hasUrl) {
            Log::warning('GraduateVideo store: missing file and url', [
                'upload_limits' => $this->resolveUploadLimits()['breakdown'],
            ]);

            return back()->withErrors(['video_file' => 'يرجى رفع ملف فيديو أو إدخال رابط (YouTube / Vimeo / رابط مباشر).'])->withInput();
        }

        if ($hasFile && ! $this->allowedUploadExtension($request->file('video_file'))) {
            $this->logGraduateVideoUploadContext($request, 'store_bad_extension');

            return back()->withErrors(['video_file' => 'امتداد غير مدعوم. استخدم: '.implode(', ', self::ALLOWED_EXTENSIONS)])->withInput();
        }

        $hasThumb = $request->hasFile('thumbnail_image') && $request->file('thumbnail_image')->isValid();
        if ($hasThumb && ! GraduateVideo::hasDatabaseColumn('thumbnail_path')) {
            return back()->withErrors(['thumbnail_image' => 'عمود الصورة غير موجود في الجدول. نفِّذ php artisan migrate أو احذف ملف الصورة من الطلب.'])->withInput();
        }
        if ($hasThumb && ! $this->allowedThumbExtension($request->file('thumbnail_image'))) {
            return back()->withErrors(['thumbnail_image' => 'استخدم صورة: '.implode(', ', self::ALLOWED_THUMB_EXTENSIONS)])->withInput();
        }

        $storedPath = null;
        $videoUrl = $hasUrl ? trim($request->input('video_url')) : '';

        if ($hasFile) {
            try {
                $storedPath = $this->storeUploadedVideo($request->file('video_file'));
                Log::info('GraduateVideo store: file saved', ['stored_path' => $storedPath]);
            } catch (\Throwable $e) {
                Log::error('GraduateVideo store: move failed', [
                    'message' => $e->getMessage(),
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                ]);

                return back()->withErrors(['video_file' => 'فشل حفظ الملف على السيرفر. راجع السجلات.'])->withInput();
            }
            $videoUrl = '';
        }

        $thumbPath = null;
        if ($hasThumb) {
            try {
                $thumbPath = $this->storeUploadedThumbnail($request->file('thumbnail_image'));
            } catch (\Throwable $e) {
                Log::error('GraduateVideo thumbnail save failed', ['message' => $e->getMessage()]);

                return back()->withErrors(['thumbnail_image' => 'فشل حفظ الصورة.'])->withInput();
            }
        }

        $enabled = $request->has('enabled');

        GraduateVideo::create(GraduateVideo::onlyExistingColumns([
            'title' => $request->input('title'),
            'slug' => trim((string) $request->input('slug')) ?: null,
            'description' => $request->input('description'),
            'duration_seconds' => GraduateVideo::parseDurationInput($request->input('duration')),
            'thumbnail_path' => $thumbPath,
            'video_url' => $videoUrl,
            'stored_video_path' => $storedPath,
            'sort_order' => $request->input('sort_order', 0),
            'enabled' => $enabled,
        ]));

        return redirect('/admin/additional_page/graduate_videos')->with('success', 'تم حفظ الفيديو');
    }

    public function edit($id)
    {
        $this->authorize('admin_additional_pages');

        $video = GraduateVideo::findOrFail($id);

        $data = [
            'pageTitle' => 'تعديل فيديو خريج',
            'video' => $video,
            'maxUploadMb' => round($this->effectiveMaxUploadKb() / 1024, 1),
            'phpUploadIni' => ini_get('upload_max_filesize'),
            'phpPostIni' => ini_get('post_max_size'),
        ];

        return view('admin.graduate_videos.edit', $data);
    }

    public function update(Request $request, $id)
    {
        $this->authorize('admin_additional_pages');

        @set_time_limit(600);

        $this->logGraduateVideoUploadContext($request, 'update_begin', ['video_id' => $id]);

        $video = GraduateVideo::findOrFail($id);

        $maxKb = $this->effectiveMaxUploadKb();
        try {
            $request->validate(
                $this->graduateVideoValidationRules($maxKb, $video),
                $this->graduateVideoFileValidationMessages($maxKb)
            );
        } catch (ValidationException $e) {
            $this->logGraduateVideoUploadContext($request, 'update_validation_failed', [
                'video_id' => $id,
                'validation_errors' => $e->errors(),
            ]);
            Log::warning('GraduateVideoValidationFailed', [
                'action' => 'update',
                'video_id' => $id,
                'errors' => $e->errors(),
                'upload_limits' => $this->resolveUploadLimits()['breakdown'],
            ]);

            throw $e;
        }

        $hasFile = $request->hasFile('video_file') && $request->file('video_file')->isValid();
        if ($hasFile && ! $this->allowedUploadExtension($request->file('video_file'))) {
            $this->logGraduateVideoUploadContext($request, 'update_bad_extension', ['video_id' => $id]);

            return back()->withErrors(['video_file' => 'امتداد غير مدعوم. استخدم: '.implode(', ', self::ALLOWED_EXTENSIONS)])->withInput();
        }

        $this->logGraduateVideoUploadContext($request, 'update_after_validation', [
            'video_id' => $id,
            'has_file_valid' => $hasFile,
        ]);

        $removeStored = $request->boolean('remove_stored_file');
        $videoUrl = trim((string) $request->input('video_url', ''));

        $storedPath = $video->stored_video_path;

        if ($removeStored && $storedPath) {
            $this->deleteStoredFile($storedPath);
            $storedPath = null;
        }

        if ($hasFile) {
            $this->deleteStoredFile($storedPath);
            try {
                $storedPath = $this->storeUploadedVideo($request->file('video_file'));
                Log::info('GraduateVideo update: file saved', ['video_id' => $id, 'stored_path' => $storedPath]);
            } catch (\Throwable $e) {
                Log::error('GraduateVideo update: move failed', [
                    'video_id' => $id,
                    'message' => $e->getMessage(),
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                ]);

                return back()->withErrors(['video_file' => 'فشل حفظ الملف على السيرفر. راجع السجلات.'])->withInput();
            }
            $videoUrl = '';
        }

        if (! $storedPath && ! $hasFile && strlen($videoUrl) === 0) {
            return back()->withErrors(['video_url' => 'أدخل رابط فيديو أو ارفع ملفاً، أو أبقِ الملف المرفوع الحالي دون حذفه.'])->withInput();
        }

        $hasThumb = $request->hasFile('thumbnail_image') && $request->file('thumbnail_image')->isValid();
        if ($hasThumb && ! GraduateVideo::hasDatabaseColumn('thumbnail_path')) {
            return back()->withErrors(['thumbnail_image' => 'عمود الصورة غير موجود في الجدول. نفِّذ php artisan migrate أو احذف ملف الصورة.'])->withInput();
        }
        if ($hasThumb && ! $this->allowedThumbExtension($request->file('thumbnail_image'))) {
            return back()->withErrors(['thumbnail_image' => 'استخدم صورة: '.implode(', ', self::ALLOWED_THUMB_EXTENSIONS)])->withInput();
        }

        $removeThumb = $request->boolean('remove_thumbnail');
        $thumbPath = $video->thumbnail_path;

        if ($removeThumb && $thumbPath) {
            $this->deleteStoredFile($thumbPath);
            $thumbPath = null;
        }

        if ($hasThumb) {
            $this->deleteStoredFile($thumbPath);
            try {
                $thumbPath = $this->storeUploadedThumbnail($request->file('thumbnail_image'));
            } catch (\Throwable $e) {
                Log::error('GraduateVideo thumbnail update failed', ['video_id' => $id, 'message' => $e->getMessage()]);

                return back()->withErrors(['thumbnail_image' => 'فشل حفظ الصورة.'])->withInput();
            }
        }

        $slugInput = trim((string) $request->input('slug'));
        $video->update(GraduateVideo::onlyExistingColumns([
            'title' => $request->input('title'),
            'slug' => $slugInput !== '' ? $slugInput : null,
            'description' => $request->input('description'),
            'duration_seconds' => GraduateVideo::parseDurationInput($request->input('duration')),
            'thumbnail_path' => $thumbPath,
            'video_url' => $storedPath ? '' : $videoUrl,
            'stored_video_path' => $storedPath,
            'sort_order' => $request->input('sort_order', 0),
            'enabled' => $request->boolean('enabled'),
        ]));

        return redirect('/admin/additional_page/graduate_videos')->with('success', 'تم تحديث الفيديو');
    }

    public function destroy($id)
    {
        $this->authorize('admin_additional_pages');

        $video = GraduateVideo::findOrFail($id);
        $this->deleteStoredFile($video->stored_video_path);
        $this->deleteStoredFile($video->thumbnail_path);
        $video->delete();

        return redirect('/admin/additional_page/graduate_videos')->with('success', 'تم حذف الفيديو');
    }

    /**
     * قواعد تحقق حسب الأعمدة الموجودة فعلياً في الجدول (قبل تشغيل كل الهجرات).
     */
    private function graduateVideoValidationRules(int $maxKb, ?GraduateVideo $videoForUpdate = null): array
    {
        $rules = [
            'title' => 'required|string|max:255',
            'video_url' => 'nullable|string|max:2000',
            'video_file' => 'nullable|file|max:'.$maxKb,
            'sort_order' => 'nullable|integer|min:0',
            'duration' => 'nullable|string|max:32',
            'thumbnail_image' => 'nullable|image|max:2048',
        ];

        if (GraduateVideo::hasDatabaseColumn('slug')) {
            $rules['slug'] = $videoForUpdate === null
                ? ['nullable', 'string', 'max:191', 'regex:/^[a-z0-9\-]*$/', 'unique:graduate_videos,slug']
                : ['nullable', 'string', 'max:191', 'regex:/^[a-z0-9\-]*$/', Rule::unique('graduate_videos', 'slug')->ignore($videoForUpdate->id)];
        }

        if (GraduateVideo::hasDatabaseColumn('description')) {
            $rules['description'] = 'nullable|string|max:5000';
        }

        return $rules;
    }

    /**
     * أصغر من: إعداد Laravel و upload_max_filesize و post_max_size.
     */
    private function effectiveMaxUploadKb(): int
    {
        return $this->resolveUploadLimits()['effective_kb'];
    }

    /**
     * @return array{effective_kb: int, breakdown: array}
     */
    private function resolveUploadLimits(): array
    {
        if ($this->resolvedUploadLimits !== null) {
            return $this->resolvedUploadLimits;
        }

        $configRaw = config('graduate_videos.max_upload_kb');
        // على السيرفر إن لم يُنشر config/graduate_videos.php أو الكاش قديم يكون null → (int)null = 0 → كان يصبح الحد 1024 ك.ب فقط
        $configKb = max(1024, (int) ($configRaw !== null && $configRaw !== '' ? $configRaw : 1048576));
        $configFallback = $configRaw === null || $configRaw === '';
        $uploadRaw = ini_get('upload_max_filesize');
        $postRaw = ini_get('post_max_size');
        $uploadKb = $this->phpIniValueToKb($uploadRaw);
        $postKb = $this->phpIniValueToKb($postRaw);
        $postBodyCap = $postKb > 0 ? max(1, $postKb - 512) : 0;

        $envVal = env('GRADUATE_VIDEO_MAX_KB');
        $envDisplay = ($envVal === null || $envVal === '') ? '(غير مضبوط في .env — يُستخدم الافتراضي داخل config)' : (string) $envVal;

        if (! config('graduate_videos.respect_php_upload_limits', true)) {
            return $this->resolvedUploadLimits = [
                'effective_kb' => $configKb,
                'breakdown' => [
                    'env_GRADUATE_VIDEO_MAX_KB' => $envDisplay,
                    'config_max_kb' => $configKb,
                    'config_raw_laravel' => $configRaw,
                    'config_fallback_1048576_applied' => $configFallback,
                    'respect_php' => false,
                    'bottleneck' => 'config_only (تجاهل حدود PHP)',
                    'upload_ini' => $uploadRaw,
                    'post_ini' => $postRaw,
                    'effective_max_kb' => $configKb,
                    'effective_max_mb' => round($configKb / 1024, 2),
                ],
            ];
        }

        $phpCapKb = $configKb;
        $bottleneck = 'إعداد التطبيق (config / GRADUATE_VIDEO_MAX_KB)';

        if ($uploadKb > 0 && $uploadKb < $phpCapKb) {
            $phpCapKb = $uploadKb;
            $bottleneck = 'upload_max_filesize (PHP)';
        }
        if ($postBodyCap > 0 && $postBodyCap < $phpCapKb) {
            $phpCapKb = $postBodyCap;
            $bottleneck = 'post_max_size (PHP) — غالباً هو الأضيق على cPanel';
        }

        $effective = max(1024, $phpCapKb);

        return $this->resolvedUploadLimits = [
            'effective_kb' => $effective,
            'breakdown' => [
                'env_GRADUATE_VIDEO_MAX_KB' => $envDisplay,
                'config_max_kb' => $configKb,
                'config_raw_laravel' => $configRaw,
                'config_fallback_1048576_applied' => $configFallback,
                'respect_php' => true,
                'upload_ini' => $uploadRaw,
                'upload_parsed_kb' => $uploadKb,
                'post_ini' => $postRaw,
                'post_parsed_kb' => $postKb,
                'post_body_cap_kb' => $postBodyCap,
                'bottleneck' => $bottleneck,
                'effective_max_kb' => $effective,
                'effective_max_mb' => round($effective / 1024, 2),
            ],
        ];
    }

    private function phpIniValueToKb(?string $value): int
    {
        $value = trim((string) $value);
        if ($value === '' || $value === '0') {
            return 0;
        }

        if (preg_match('/^(\d+(?:\.\d+)?)\s*([gmk]?)\s*b?$/i', $value, $m)) {
            $num = (float) $m[1];
            $unit = strtolower($m[2] ?? '');

            switch ($unit) {
                case 'g':
                    return max(1, (int) floor($num * 1024 * 1024));
                case 'm':
                    return max(1, (int) floor($num * 1024));
                case 'k':
                    return max(1, (int) floor($num));
                default:
                    return max(1, (int) floor($num / 1024));
            }
        }

        $asFloat = (float) $value;

        return $asFloat > 0 ? max(1, (int) floor($asFloat / 1024)) : 0;
    }

    private function graduateVideoFileValidationMessages(int $maxKb): array
    {
        $maxKb = max(1, $maxKb);
        $mb = round($maxKb / 1024, 2);
        if ($mb < 0.01) {
            $mb = round($maxKb / 1024, 4);
        }
        $uploadIni = ini_get('upload_max_filesize');
        $postIni = ini_get('post_max_size');

        return [
            'video_file.max' => 'حجم الفيديو يتجاوز الحد المسموح حالياً: ≈ '.$mb.' ميجابايت ('.$maxKb.' ك.ب). '
                .'في cPanel: Software → MultiPHP INI Editor — اجعل post_max_size أكبر من upload_max_filesize (مثلاً 520M و 512M). '
                .'أو استخدم رابط YouTube. قيم PHP الآن: upload_max_filesize='.$uploadIni.', post_max_size='.$postIni.'.',
        ];
    }

    private function allowedUploadExtension($uploadedFile): bool
    {
        $ext = strtolower($uploadedFile->getClientOriginalExtension());

        return in_array($ext, self::ALLOWED_EXTENSIONS, true);
    }

    private function allowedThumbExtension($uploadedFile): bool
    {
        $ext = strtolower($uploadedFile->getClientOriginalExtension());

        return in_array($ext, self::ALLOWED_THUMB_EXTENSIONS, true);
    }

    private function storeUploadedThumbnail($uploadedFile): string
    {
        $ext = strtolower($uploadedFile->getClientOriginalExtension());
        if (! in_array($ext, self::ALLOWED_THUMB_EXTENSIONS, true)) {
            $ext = 'jpg';
        }

        $dir = public_path('store/graduate_videos/thumbnails');
        if (! is_dir($dir)) {
            @mkdir($dir, 0755, true);
        }

        $filename = Str::random(24).'.'.$ext;
        $targetPath = $dir.DIRECTORY_SEPARATOR.$filename;

        $uploadedFile->move($dir, $filename);

        if (! is_file($targetPath)) {
            throw new \RuntimeException('Thumbnail file missing after move.');
        }

        return 'store/graduate_videos/thumbnails/'.$filename;
    }

    private function storeUploadedVideo($uploadedFile): string
    {
        $ext = strtolower($uploadedFile->getClientOriginalExtension());
        if (! in_array($ext, self::ALLOWED_EXTENSIONS, true)) {
            $ext = 'mp4';
        }

        $dir = public_path('store/graduate_videos');
        if (! is_dir($dir)) {
            $created = @mkdir($dir, 0755, true);
            Log::info('GraduateVideo mkdir store/graduate_videos', [
                'dir' => $dir,
                'created' => $created,
                'writable' => is_dir($dir) ? is_writable($dir) : false,
            ]);
        }

        $filename = Str::random(32).'.'.$ext;
        $targetPath = $dir.DIRECTORY_SEPARATOR.$filename;

        Log::info('GraduateVideo move attempt', [
            'target' => $targetPath,
            'tmp_path' => method_exists($uploadedFile, 'getRealPath') ? $uploadedFile->getRealPath() : null,
            'size' => $uploadedFile->getSize(),
        ]);

        $uploadedFile->move($dir, $filename);

        if (! is_file($targetPath)) {
            Log::error('GraduateVideo: move reported success but file missing', ['target' => $targetPath]);
            throw new \RuntimeException('Uploaded file not found after move.');
        }

        return 'store/graduate_videos/'.$filename;
    }

    /**
     * Diagnostic logging for video uploads — check storage/logs/laravel.log for "GraduateVideo".
     */
    private function logGraduateVideoUploadContext(Request $request, string $phase, array $extra = []): void
    {
        $limits = $this->resolveUploadLimits();
        $effectiveKb = $limits['effective_kb'];

        $uploadedFile = $request->file('video_file');

        $filesFromInput = [];
        if (isset($_FILES['video_file'])) {
            $vf = $_FILES['video_file'];
            $filesFromInput = [
                'name' => $vf['name'] ?? null,
                'type' => $vf['type'] ?? null,
                'size' => $vf['size'] ?? null,
                'size_mb_approx' => isset($vf['size']) ? round(((int) $vf['size']) / 1024 / 1024, 2) : null,
                'error' => $vf['error'] ?? null,
                'error_label' => $this->phpUploadErrorLabel((int) ($vf['error'] ?? -1)),
            ];
            if (isset($vf['size']) && (int) $vf['size'] > 0) {
                $limitBytes = $effectiveKb * 1024;
                $filesFromInput['exceeds_laravel_effective_limit'] = ((int) $vf['size']) > $limitBytes;
                $filesFromInput['effective_limit_bytes'] = $limitBytes;
            }
        }

        $ct = (string) $request->header('Content-Type');
        $multipartOk = stripos($ct, 'multipart/form-data') !== false;

        $payload = array_merge([
            'phase' => $phase,
            'method' => $request->method(),
            'path' => $request->path(),
            'full_url' => $request->fullUrl(),
            'content_type' => $request->header('Content-Type'),
            'content_length_header' => $request->header('Content-Length'),
            'content_length_mb_approx' => $request->header('Content-Length')
                ? round(((int) $request->header('Content-Length')) / 1024 / 1024, 2)
                : null,
            'multipart_form_data_header_ok' => $multipartOk,
            'expects_json' => $request->expectsJson(),
            'has_file_video_file' => $request->hasFile('video_file'),
            'all_files_keys' => array_keys($request->allFiles()),
            'post_field_keys_sample' => array_slice(array_keys($request->except(['video_file'])), 0, 15),
            '_files_video_input' => $filesFromInput,
            'php_sapi' => \PHP_SAPI,
            'php_version' => PHP_VERSION,
            'sys_temp_dir' => sys_get_temp_dir(),
            'sys_temp_writable' => @is_writable(sys_get_temp_dir()),
            'php_upload_max_filesize' => ini_get('upload_max_filesize'),
            'php_post_max_size' => ini_get('post_max_size'),
            'php_max_file_uploads' => ini_get('max_file_uploads'),
            'php_memory_limit' => ini_get('memory_limit'),
            'laravel_config_max_upload_kb' => config('graduate_videos.max_upload_kb'),
            'laravel_config_max_after_fallback_kb' => $limits['breakdown']['config_max_kb'],
            'config_fallback_applied' => (bool) ($limits['breakdown']['config_fallback_1048576_applied'] ?? false),
            'upload_limits_breakdown' => $limits['breakdown'],
            'effective_max_upload_kb' => $effectiveKb,
        ], $extra);

        if ($uploadedFile !== null) {
            $payload['uploaded_file'] = [
                'valid' => $uploadedFile->isValid(),
                'error' => $uploadedFile->getError(),
                'error_message' => $uploadedFile->getErrorMessage(),
                'original_name' => $uploadedFile->getClientOriginalName(),
                'client_mime' => $uploadedFile->getClientMimeType(),
                'size' => $uploadedFile->getSize(),
                'size_mb_approx' => $uploadedFile->getSize() ? round($uploadedFile->getSize() / 1024 / 1024, 2) : null,
                'extension' => $uploadedFile->getClientOriginalExtension(),
            ];
        }

        Log::info('GraduateVideoUpload', $payload);
    }

    private function phpUploadErrorLabel(int $code): string
    {
        $map = [
            UPLOAD_ERR_OK => 'UPLOAD_ERR_OK',
            UPLOAD_ERR_INI_SIZE => 'UPLOAD_ERR_INI_SIZE (exceeds upload_max_filesize)',
            UPLOAD_ERR_FORM_SIZE => 'UPLOAD_ERR_FORM_SIZE (exceeds MAX_FILE_SIZE)',
            UPLOAD_ERR_PARTIAL => 'UPLOAD_ERR_PARTIAL',
            UPLOAD_ERR_NO_FILE => 'UPLOAD_ERR_NO_FILE',
            UPLOAD_ERR_NO_TMP_DIR => 'UPLOAD_ERR_NO_TMP_DIR',
            UPLOAD_ERR_CANT_WRITE => 'UPLOAD_ERR_CANT_WRITE',
            UPLOAD_ERR_EXTENSION => 'UPLOAD_ERR_EXTENSION',
        ];

        return $map[$code] ?? 'UNKNOWN_'.$code;
    }

    private function deleteStoredFile(?string $relativePath): void
    {
        if (empty($relativePath)) {
            return;
        }
        $relativePath = ltrim($relativePath, '/');
        $full = public_path($relativePath);
        if (is_file($full)) {
            @unlink($full);
        }
    }
}
