<?php
declare(strict_types=1);

/**
 * ImageProcessor — Secure image handling for product uploads.
 *
 * - Strips ALL EXIF/metadata
 * - Resizes to fit target dimensions (cover-crop from center)
 * - Converts to WebP (quality 85)
 * - Saves with hashed filename (no original name leakage)
 */
class ImageProcessor
{
    /**
     * Process an uploaded image file.
     *
     * @param array  $uploadedFile  $_FILES['field'] entry
     * @param string $targetDir     Absolute directory to save into
     * @param int    $maxWidth      Target width (default 800)
     * @param int    $maxHeight     Target height (default 800)
     * @return string|false         Relative web path on success, false on failure
     */
    public static function process(array $uploadedFile, string $targetDir, int $maxWidth = 480, int $maxHeight = 480)
    {
        if (empty($uploadedFile['tmp_name']) || $uploadedFile['error'] !== UPLOAD_ERR_OK) {
            return false;
        }

        $tmpPath = $uploadedFile['tmp_name'];

        // Validate it's actually an image
        $info = @getimagesize($tmpPath);
        if (!$info) {
            return false;
        }

        $mime = $info['mime'] ?? '';
        $allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/bmp'];
        if (!in_array($mime, $allowedMimes, true)) {
            return false;
        }

        // Max file size: 10MB
        if (filesize($tmpPath) > 10 * 1024 * 1024) {
            return false;
        }

        // Load the image — this inherently strips EXIF since GD doesn't preserve it
        $source = self::loadImage($tmpPath, $mime);
        if (!$source) {
            return false;
        }

        $origWidth  = imagesx($source);
        $origHeight = imagesy($source);

        // Calculate cover-crop dimensions (fill target area, crop excess)
        $resized = self::coverCrop($source, $origWidth, $origHeight, $maxWidth, $maxHeight);
        imagedestroy($source);

        if (!$resized) {
            return false;
        }

        // Ensure target directory exists
        if (!is_dir($targetDir)) {
            mkdir($targetDir, 0755, true);
        }

        // Generate obfuscated filename
        $hash     = hash('sha256', random_bytes(32) . microtime(true));
        $filename = substr($hash, 0, 24) . '.webp';
        $fullPath = rtrim($targetDir, '/') . '/' . $filename;

        // Save as WebP
        $saved = imagewebp($resized, $fullPath, 85);
        imagedestroy($resized);

        if (!$saved) {
            return false;
        }

        // Set restrictive permissions
        chmod($fullPath, 0644);

        // Return the web-relative path
        // Convert /var/www/public/uploads/... → /uploads/...
        $webPath = self::toWebPath($fullPath);
        return $webPath;
    }

    /**
     * Load an image resource from file, regardless of format.
     * GD re-creates the image from pixel data, inherently stripping EXIF.
     */
    private static function loadImage(string $path, string $mime)
    {
        switch ($mime) {
            case 'image/jpeg':
                return @imagecreatefromjpeg($path);
            case 'image/png':
                $img = @imagecreatefrompng($path);
                if ($img) {
                    imagealphablending($img, true);
                    imagesavealpha($img, true);
                }
                return $img;
            case 'image/gif':
                return @imagecreatefromgif($path);
            case 'image/webp':
                return @imagecreatefromwebp($path);
            case 'image/bmp':
                return @imagecreatefrombmp($path);
            default:
                return false;
        }
    }

    /**
     * Cover-crop: resize to fill the target area, then center-crop.
     * Like CSS `object-fit: cover`.
     */
    private static function coverCrop($source, int $srcW, int $srcH, int $targetW, int $targetH)
    {
        // If source is smaller than target in both dimensions, just use source size
        if ($srcW <= $targetW && $srcH <= $targetH) {
            $targetW = $srcW;
            $targetH = $srcH;
        }

        // Calculate scale to cover target area
        $scaleX = $targetW / $srcW;
        $scaleY = $targetH / $srcH;
        $scale  = max($scaleX, $scaleY);

        // Scaled dimensions
        $scaledW = (int) ceil($srcW * $scale);
        $scaledH = (int) ceil($srcH * $scale);

        // Crop offset (center)
        $cropX = (int) (($scaledW - $targetW) / 2);
        $cropY = (int) (($scaledH - $targetH) / 2);

        // Source crop rectangle (in original image coordinates)
        $srcCropX = (int) ($cropX / $scale);
        $srcCropY = (int) ($cropY / $scale);
        $srcCropW = (int) ($targetW / $scale);
        $srcCropH = (int) ($targetH / $scale);

        // Create output image
        $output = imagecreatetruecolor($targetW, $targetH);
        if (!$output) {
            return false;
        }

        // Preserve alpha for WebP
        imagealphablending($output, false);
        imagesavealpha($output, true);

        // Resample
        $ok = imagecopyresampled(
            $output, $source,
            0, 0,                         // dest x,y
            $srcCropX, $srcCropY,         // source x,y
            $targetW, $targetH,           // dest w,h
            $srcCropW, $srcCropH          // source w,h
        );

        return $ok ? $output : false;
    }

    /**
     * Convert absolute server path to web-relative path.
     */
    private static function toWebPath(string $fullPath): string
    {
        // Try common web roots
        $roots = ['/var/www/public', '/var/www/html', STORE_ROOT ?? ''];
        foreach ($roots as $root) {
            if ($root && strpos($fullPath, $root) === 0) {
                return substr($fullPath, strlen($root));
            }
        }
        // Fallback: return from /uploads/ onwards
        $pos = strpos($fullPath, '/uploads/');
        if ($pos !== false) {
            return substr($fullPath, $pos);
        }
        return '/' . basename($fullPath);
    }

    /**
     * Delete an image file by its web path.
     */
    public static function delete(string $webPath): bool
    {
        if (empty($webPath) || $webPath === '/') {
            return false;
        }
        $roots = ['/var/www/public', '/var/www/html', STORE_ROOT ?? ''];
        foreach ($roots as $root) {
            if ($root) {
                $full = rtrim($root, '/') . '/' . ltrim($webPath, '/');
                if (file_exists($full)) {
                    return @unlink($full);
                }
            }
        }
        return false;
    }
}
