<?php

namespace KeyLair;

class ImageUpload
{
    private const ALLOWED = [
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'image/webp' => 'webp',
    ];
    private const MAX_BYTES = 8 * 1024 * 1024; // 8 MB

    /**
     * @param array $file A single entry from $_FILES (already the right sub-array, not the raw superglobal).
     * @return array{ok: bool, filename?: string, error?: string}
     */
    public static function save(array $file, string $destDir): array
    {
        if (($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
            return ['ok' => false, 'error' => 'no_file'];
        }
        if ($file['error'] !== UPLOAD_ERR_OK) {
            return ['ok' => false, 'error' => self::errorMessage($file['error'])];
        }
        if ($file['size'] > self::MAX_BYTES) {
            return ['ok' => false, 'error' => 'Image is too large (max 8 MB).'];
        }
        if (!is_uploaded_file($file['tmp_name'])) {
            return ['ok' => false, 'error' => 'Invalid upload.'];
        }

        // Validate by actually decoding the image, not just trusting the extension/mime header.
        $info = @getimagesize($file['tmp_name']);
        if ($info === false || !isset(self::ALLOWED[$info['mime']])) {
            return ['ok' => false, 'error' => 'File must be a JPEG, PNG, or WebP image.'];
        }

        $ext = self::ALLOWED[$info['mime']];
        $filename = bin2hex(random_bytes(16)) . '.' . $ext;

        if (!is_dir($destDir)) {
            mkdir($destDir, 0755, true);
        }

        if (!move_uploaded_file($file['tmp_name'], $destDir . '/' . $filename)) {
            return ['ok' => false, 'error' => 'Could not save the uploaded file.'];
        }

        return ['ok' => true, 'filename' => $filename];
    }

    /**
     * A bare "Upload failed." (the old behavior for every PHP UPLOAD_ERR_*
     * code) doesn't tell an admin why -- most commonly this single file
     * exceeded the server's own upload_max_filesize, a server-config limit
     * distinct from (and possibly smaller than) the 8 MB app-level cap this
     * class enforces itself just below. Named per PHP's UPLOAD_ERR_*
     * constants.
     */
    private static function errorMessage(int $phpUploadError): string
    {
        return match ($phpUploadError) {
            UPLOAD_ERR_INI_SIZE => 'File exceeds the server\'s per-file upload limit (upload_max_filesize in php.ini).',
            UPLOAD_ERR_FORM_SIZE => 'File exceeds the upload size limit set by the form.',
            UPLOAD_ERR_PARTIAL => 'The file was only partially uploaded -- try again.',
            UPLOAD_ERR_NO_TMP_DIR => 'Server is missing a temporary folder for uploads.',
            UPLOAD_ERR_CANT_WRITE => 'Server failed to write the uploaded file to disk.',
            UPLOAD_ERR_EXTENSION => 'A server extension blocked this upload.',
            default => 'Upload failed.',
        };
    }
}
