<?php
/**
 * Image Boost — Compressor Engine
 * Handles the actual image compression using GD or Imagick.
 */

if (!defined('ABSPATH')) {
    exit;
}

class ImgBoost_Compressor {

    /**
     * Compress a single image file.
     *
     * @param string $file_path Absolute path to the image.
     * @param string $level     Compression level: 'normal' or 'pro'.
     * @return array|WP_Error   Result with original_size, optimized_size, savings_percent.
     */
    public static function compress($file_path, $level = 'pro') {
        if (!file_exists($file_path)) {
            return new WP_Error('file_not_found', __('Image file not found.', 'image-boost'));
        }

        $mime = imgboost_get_mime_type($file_path);
        if (!$mime) {
            return new WP_Error('invalid_mime', __('Cannot determine image type.', 'image-boost'));
        }

        $quality = imgboost_get_quality($level);
        $original_size = filesize($file_path);
        $engine = imgboost_get_engine();

        if ($engine === 'none') {
            return new WP_Error('no_engine', __('No image processing engine available (GD or Imagick required).', 'image-boost'));
        }
        
        // Prevent OOM and timeouts on huge images
        wp_raise_memory_limit('image');

        // Pre-flight check for RAM
        $img_info = getimagesize($file_path);
        if ($img_info) {
            $width = $img_info[0];
            $height = $img_info[1];
            // Estimated RAM needed: W x H x 4 bytes per pixel + 10% overhead
            $memory_required = ($width * $height * 4 * 1.1); 
            $memory_limit = ini_get('memory_limit');
            
            if ($memory_limit !== '-1' && preg_match('/^(\d+)(.)$/', $memory_limit, $matches)) {
                $limit_bytes = $matches[1];
                switch (strtoupper($matches[2])) {
                    case 'G': $limit_bytes *= 1024; // fallthrough
                    case 'M': $limit_bytes *= 1024; // fallthrough
                    case 'K': $limit_bytes *= 1024;
                }
                
                if ($limit_bytes > 0 && $memory_required > $limit_bytes) {
                    return new WP_Error('oom_risk', __('Image is too large and would cause memory exhaustion. Skipped.', 'image-boost'));
                }
            }
        }
        
        if (class_exists('Imagick')) {
            try {
                // Set reasonable limits to prevent server explosion
                Imagick::setResourceLimit(Imagick::RESOURCETYPE_MEMORY, 268435456); // 256MB
                Imagick::setResourceLimit(Imagick::RESOURCETYPE_MAP, 268435456);
                
                // CRITICAL: Prevent OpenMP from using 100% of ALL CPU cores!
                Imagick::setResourceLimit(Imagick::RESOURCETYPE_THREAD, 1);
            } catch (Exception $e) {
                // ignore
            }
        }

        require_once ABSPATH . 'wp-admin/includes/file.php';
        WP_Filesystem();
        global $wp_filesystem;

        // Use a temporary file to avoid overwriting the original if size increases
        $tmp_path = $file_path . '.imgboost.tmp';
        
        $copied = false;
        if ($wp_filesystem) {
            $copied = $wp_filesystem->copy($file_path, $tmp_path, true, FS_CHMOD_FILE);
        } else {
            $copied = copy($file_path, $tmp_path);
        }

        if (!$copied) {
            return new WP_Error('tmp_error', __('Could not create temporary processing file.', 'image-boost'));
        }

        // Resize if needed (Cap max_width to safe limit e.g. 5000px)
        $max_w = min((int) $quality['max_width'], 5000);
        if ($max_w > 0) {
            self::maybe_resize($tmp_path, $max_w, $engine, $mime);
        }

        // Compress based on engine
        if ($engine === 'imagick') {
            $result = self::compress_with_imagick($tmp_path, $mime, $quality, $level);
        } else {
            $result = self::compress_with_gd($tmp_path, $mime, $quality, $level);
        }

        if (is_wp_error($result)) {
            if ($wp_filesystem) {
                $wp_filesystem->delete($tmp_path);
            } else {
                wp_delete_file($tmp_path);
            }
            return $result;
        }

        // Clear stat cache and get new size
        clearstatcache(true, $tmp_path);
        $optimized_size = filesize($tmp_path);

        // Don't keep optimized file if bigger than original
        if ($optimized_size >= $original_size) {
            if ($wp_filesystem) {
                $wp_filesystem->delete($tmp_path);
            } else {
                wp_delete_file($tmp_path);
            }
            $optimized_size = $original_size;
        } else {
            if ($wp_filesystem) {
                $wp_filesystem->move($tmp_path, $file_path, true);
            } else {
                // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename
                rename($tmp_path, $file_path);
            }
        }

        $savings = $original_size > 0
            ? round((($original_size - $optimized_size) / $original_size) * 100, 2)
            : 0;

        return [
            'original_size'   => $original_size,
            'optimized_size'  => $optimized_size,
            'savings_percent' => max(0, $savings),
            'engine'          => $engine,
            'level'           => $level,
        ];
    }

    /**
     * Compress using Imagick.
     */
    private static function compress_with_imagick($file_path, $mime, $quality, $level) {
        try {
            $image = new Imagick($file_path);

            // Strip metadata
            if ($quality['strip_meta']) {
                $image->stripImage();
            }

            switch ($mime) {
                case 'image/jpeg':
                    $image->setImageFormat('jpeg');
                    $image->setImageCompression(Imagick::COMPRESSION_JPEG);
                    // Avoid quality=100 bloat by capping at 92 if normal
                    $q = min((int) $quality['jpeg_quality'], 92);
                    $image->setImageCompressionQuality($q);
                    $image->setInterlaceScheme(Imagick::INTERLACE_PLANE);

                    if ($level !== 'normal') {
                        $image->unsharpMaskImage(0, 0.5, 1, 0.05);
                    }
                    break;

                case 'image/png':
                    $image->setImageFormat('png');
                    $image->setOption('png:compression-level', (string) $quality['png_quality']);
                    break;

                case 'image/gif':
                    // GIFs: Only strip metadata, minimal compression
                    break;

                case 'image/webp':
                    $image->setImageFormat('webp');
                    $image->setImageCompressionQuality($quality['webp_quality']);
                    break;
            }

            $image->writeImage($file_path);
            $image->destroy();

            return true;

        } catch (Exception $e) {
            return new WP_Error('imagick_error', $e->getMessage());
        }
    }

    /**
     * Compress using GD Library.
     */
    private static function compress_with_gd($file_path, $mime, $quality, $level) {
        switch ($mime) {
            case 'image/jpeg':
                if ( ! function_exists( 'imagecreatefromjpeg' ) ) {
                    return new WP_Error('gd_error', __('GD library imagecreatefromjpeg is not available.', 'image-boost'));
                }
                $image = imagecreatefromjpeg($file_path);
                if (!$image) {
                    return new WP_Error('gd_error', __('Failed to load JPEG image.', 'image-boost'));
                }

                imageinterlace($image, true);

                if ($level !== 'normal') {
                    $sharpen = [
                        [-1, -1, -1],
                        [-1, 20, -1],
                        [-1, -1, -1],
                    ];
                    $divisor = array_sum(array_map('array_sum', $sharpen));
                    if (function_exists('imageconvolution')) {
                        imageconvolution($image, $sharpen, $divisor, 0);
                    }
                }

                $q = min((int) $quality['jpeg_quality'], 92);
                if (function_exists('imagejpeg')) {
                    imagejpeg($image, $file_path, $q);
                }
                if (function_exists('imagedestroy')) {
                    imagedestroy($image);
                }
                break;

            case 'image/png':
                if ( ! function_exists( 'imagecreatefrompng' ) ) {
                    return new WP_Error('gd_error', __('GD library imagecreatefrompng is not available.', 'image-boost'));
                }
                $image = imagecreatefrompng($file_path);
                if (!$image) {
                    return new WP_Error('gd_error', __('Failed to load PNG image.', 'image-boost'));
                }

                if (function_exists('imagesavealpha')) {
                    imagesavealpha($image, true);
                }
                if (function_exists('imagealphablending')) {
                    imagealphablending($image, false);
                }

                if (function_exists('imagepng')) {
                    imagepng($image, $file_path, $quality['png_quality']);
                }
                if (function_exists('imagedestroy')) {
                    imagedestroy($image);
                }
                break;

            case 'image/gif':
                if (function_exists('imagecreatefromgif')) {
                    $image = imagecreatefromgif($file_path);
                    if ($image) {
                        if (function_exists('imagegif')) {
                            imagegif($image, $file_path);
                        }
                        if (function_exists('imagedestroy')) {
                            imagedestroy($image);
                        }
                    }
                }
                break;

            case 'image/webp':
                if ( ! function_exists( 'imagecreatefromwebp' ) ) {
                    return new WP_Error('gd_error', __('GD library imagecreatefromwebp is not available.', 'image-boost'));
                }
                $image = imagecreatefromwebp($file_path);
                if (!$image) {
                    return new WP_Error('gd_error', __('Failed to load WebP image.', 'image-boost'));
                }
                if (function_exists('imagewebp')) {
                    imagewebp($image, $file_path, $quality['webp_quality']);
                }
                if (function_exists('imagedestroy')) {
                    imagedestroy($image);
                }
                break;
        }

        return true;
    }

    /**
     * Maybe resize image if it exceeds max width/height.
     */
    private static function maybe_resize($file_path, $max_width, $engine, $mime) {
        if (!get_option('imgboost_resize_enabled', 1)) {
            return;
        }

        if ( ! function_exists( 'getimagesize' ) ) {
            return;
        }
        $size = getimagesize($file_path);
        if (!$size) {
            return;
        }

        $orig_w = $size[0];
        $orig_h = $size[1];
        
        // If neither dimension exceeds max_width, skip
        if ($orig_w <= $max_width && $orig_h <= $max_width) {
            return;
        }

        // Calculate new dims maintaining aspect ratio
        if ($orig_w > $orig_h) {
            $new_w = $max_width;
            $new_h = (int) round(($orig_h * $new_w) / $orig_w);
        } else {
            $new_h = $max_width;
            $new_w = (int) round(($orig_w * $new_h) / $orig_h);
        }

        if ($engine === 'imagick') {
            try {
                $img = new Imagick($file_path);
                $img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
                $img->writeImage($file_path);
                $img->destroy();
            } catch (Exception $e) {
                // Silently fail resize
            }
        } else {
            $src = null;
            switch ($mime) {
                case 'image/jpeg':
                    $src = imagecreatefromjpeg($file_path);
                    break;
                case 'image/png':
                    $src = imagecreatefrompng($file_path);
                    break;
                case 'image/webp':
                    $src = imagecreatefromwebp($file_path);
                    break;
            }

            if ($src) {
                $dst = imagecreatetruecolor($new_w, $new_h);

                if ($mime === 'image/png') {
                    imagealphablending($dst, false);
                    imagesavealpha($dst, true);
                    $transparent = imagecolorallocatealpha($dst, 0, 0, 0, 127);
                    imagefilledrectangle($dst, 0, 0, $new_w, $new_h, $transparent);
                }

                imagecopyresampled($dst, $src, 0, 0, 0, 0, $new_w, $new_h, $orig_w, $orig_h);

                switch ($mime) {
                    case 'image/jpeg':
                        imagejpeg($dst, $file_path, 92);
                        break;
                    case 'image/png':
                        imagepng($dst, $file_path, 6);
                        break;
                    case 'image/webp':
                        imagewebp($dst, $file_path, 90);
                        break;
                }

                imagedestroy($src);
                imagedestroy($dst);
            }
        }
    }
}
