<?php

/**
 * Advanced Image Optimization Engine
 *
 * Next-generation image optimization with multiple backends and advanced features
 *
 * @package ProRank\SEO\Core\ImageOptimization
 * @since   1.0.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Core\ImageOptimization;

use ProRank\SEO\Plugin;

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


/**
 * OptimizationEngine class
 */


class OptimizationEngine {
	
	/**
	 * Available optimization tools
	 *
	 * @var array
	 */
	private array $available_tools = [];

	/**
	 * Free plugin image optimization is local-only.
	 *
	 * @param mixed $method Requested method.
	 * @return string
	 */
	private function normalize_optimization_method( $method ): string {
		return 'local';
	}
	
	/**
	 * Plugin instance
	 *
	 * @var Plugin
	 */
	private Plugin $plugin;
	
	/**
	 * Constructor
	 */
	public function __construct() {
		$this->plugin = Plugin::get_instance();
		$this->detect_available_tools();
	}
	
	/**
	 * Detect available optimization tools on the system
	 */
	private function detect_available_tools(): void {
		// PHP Extensions
		if ( extension_loaded( 'gd' ) ) {
			$this->available_tools['gd'] = [
				'name' => 'GD Library',
				'priority' => 10,
				'formats' => [ 'jpeg', 'jpg', 'png', 'gif', 'webp' ],
			];
		}
		
		if ( extension_loaded( 'imagick' ) ) {
			try {
				$imagick = new \Imagick();
				$formats = $imagick->queryFormats();
				$this->available_tools['imagick'] = [
					'name' => 'ImageMagick',
					'priority' => 50,
					'formats' => array_map( 'strtolower', $formats ),
				];
			} catch ( \Exception $e ) {
				// Imagick extension loaded but not functional (common on shared hosting)
			}
		}
	}
	
	/**
	 * Check if a binary is available
	 *
	 * @param string $binary Binary name.
	 * @return bool
	 */
	private function is_binary_available( string $binary ): bool {
		return false;
	}
	
	/**
	 * Optimize image using the best available method
	 *
	 * @param int $attachment_id Attachment ID.
	 * @return array
	 */
	public function optimize_image( int $attachment_id ): array {
		$file_path = get_attached_file( $attachment_id );
		if ( ! $file_path || ! file_exists( $file_path ) ) {
			return [
				'success' => false,
				'error' => __( 'File not found', 'prorank-seo' ),
			];
		}
		
		// Get settings
		$settings = $this->plugin->settings();
		$image_settings = get_option( 'prorank_image_optimization_settings', [] );
		$compression_type = $settings->get( 'images_compression_type', 'glossy' );
		$smart_compression = $settings->get( 'images_smart_compression', true );
		$backup_originals = $settings->get( 'images_backup_originals', true );
		
		// Backup original if enabled
		if ( $backup_originals ) {
			$this->backup_original( $file_path );
		}
		
		// Get file info
		$mime_type = wp_check_filetype( $file_path )['type'];
		$original_size = filesize( $file_path );
		$temp_backup   = $this->create_temporary_backup( $file_path );
		
		// Choose optimization method based on settings
		$optimization_method = $this->normalize_optimization_method( $image_settings['optimization_method'] ?? 'local' );
		
		// Use cloud optimization if enabled
		if ( $optimization_method === 'prorank' || $optimization_method === 'cloud' ) {
			$format = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) );
			if ( $format === 'jpg' ) {
				$format = 'jpeg';
			}
			$quality = (int) $this->get_quality_for_compression_type( $compression_type, $format );
			$result = $this->convert_via_cloud( $file_path, $format, $quality, $compression_type, $smart_compression );
		} else {
			// Use local tools
			$result = $this->optimize_with_best_tool( $file_path, $mime_type, [
				'compression_type' => $compression_type,
				'smart_compression' => $smart_compression,
				'png_pipeline' => ! empty( $image_settings['png_pipeline'] ),
				'png_quality' => (int) ( $image_settings['png_quality'] ?? 80 ),
			] );
		}
		
		$result = $this->enforce_non_regression_size( $file_path, $original_size, $temp_backup, $result );

		if ( $result['success'] ) {
			// Update metadata
			clearstatcache( true, $file_path );
			$new_size = filesize( $file_path );
			$saved_bytes = $original_size - $new_size;
			$saved_percent = ( $saved_bytes / $original_size ) * 100;
			
			// Store optimization data
			update_post_meta( $attachment_id, '_prorank_optimized', true );
			update_post_meta( $attachment_id, '_prorank_optimization_savings', $saved_bytes );
			update_post_meta( $attachment_id, '_prorank_optimization_percent', round( $saved_percent, 2 ) );
			update_post_meta( $attachment_id, '_prorank_original_size', $original_size );
			update_post_meta( $attachment_id, '_prorank_optimized_size', $new_size );
			update_post_meta( $attachment_id, '_prorank_optimization_date', current_time( 'mysql' ) );
			update_post_meta( $attachment_id, '_prorank_optimization_method', $result['method'] );
			
			// Handle WebP/AVIF conversion if enabled
			$this->handle_modern_formats( $attachment_id, $file_path );
			
			return [
				'success' => true,
				'original_size' => size_format( $original_size ),
				'new_size' => size_format( $new_size ),
				'saved_size' => size_format( $saved_bytes ),
				'saved_percent' => round( $saved_percent, 2 ),
				'method' => $result['method'],
			];
		}
		
		return $result;
	}

	/**
	 * Optimize a file path using configured options.
	 * This is used by the bulk/auto pipeline to apply advanced encoders.
	 *
	 * @param string $file_path File path.
	 * @param array  $options   Options (compression_type, smart_compression, jpegli_enabled, optimization_method).
	 * @return array
	 */
	public function optimize_file_path( string $file_path, array $options = [] ): array {
		if ( ! $file_path || ! file_exists( $file_path ) ) {
			return [
				'success' => false,
				'error' => __( 'File not found', 'prorank-seo' ),
			];
		}

		$original_size = (int) filesize( $file_path );
		$temp_backup   = $this->create_temporary_backup( $file_path );

		$defaults = [
			'compression_type' => 'glossy',
			'smart_compression' => true,
			'jpegli_enabled' => false,
			'png_pipeline' => false,
			'png_quality' => 80,
			'optimization_method' => 'local',
		];
		$options = wp_parse_args( $options, $defaults );
		$options['optimization_method'] = $this->normalize_optimization_method( $options['optimization_method'] ?? 'local' );

		$mime_type = wp_check_filetype( $file_path )['type'] ?? '';
		if ( empty( $mime_type ) ) {
			return [
				'success' => false,
				'error' => __( 'Unknown file type', 'prorank-seo' ),
			];
		}

		$extension = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) );

		$result = $this->optimize_with_best_tool( $file_path, $mime_type, $options );

		return $this->enforce_non_regression_size( $file_path, $original_size, $temp_backup, $result );
	}

	/**
	 * Create a temporary backup copy of a file before in-place optimization.
	 *
	 * @param string $file_path Source file path.
	 * @return string Temporary path or empty string if backup creation fails.
	 */
	private function create_temporary_backup( string $file_path ): string {
		if ( ! function_exists( 'wp_tempnam' ) ) {
			$file_api = ABSPATH . 'wp-admin/includes/file.php';
			if ( file_exists( $file_api ) ) {
				require_once $file_api;
			}
		}

		$temp_file = function_exists( 'wp_tempnam' )
			? \wp_tempnam( basename( $file_path ) )
			: tempnam( sys_get_temp_dir(), 'prorank_' );
		if ( ! $temp_file ) {
			return '';
		}

		if ( ! @copy( $file_path, $temp_file ) ) {
			\ProRank\SEO\Core\FilesystemHelper::delete_file( $temp_file );
			return '';
		}

		return $temp_file;
	}

	/**
	 * Prevent optimization regressions by restoring original bytes when output grows.
	 *
	 * @param string $file_path     Optimized file path.
	 * @param int    $original_size Original file size.
	 * @param string $temp_backup   Temporary backup file path.
	 * @param array  $result        Optimization result payload.
	 * @return array
	 */
	private function enforce_non_regression_size( string $file_path, int $original_size, string $temp_backup, array $result ): array {
		if ( empty( $result['success'] ) ) {
			if ( $temp_backup !== '' && file_exists( $temp_backup ) ) {
				\ProRank\SEO\Core\FilesystemHelper::delete_file( $temp_backup );
			}
			return $result;
		}

		clearstatcache( true, $file_path );
		$new_size = file_exists( $file_path ) ? (int) filesize( $file_path ) : $original_size;

		if ( $new_size > $original_size ) {
			if ( $temp_backup !== '' && file_exists( $temp_backup ) ) {
				@copy( $temp_backup, $file_path );
				\ProRank\SEO\Core\FilesystemHelper::delete_file( $temp_backup );
			}

			return [
				'success' => false,
				'method'  => $result['method'] ?? 'local',
				'error'   => __( 'Optimization increased file size; original restored.', 'prorank-seo' ),
			];
		}

		if ( $temp_backup !== '' && file_exists( $temp_backup ) ) {
			\ProRank\SEO\Core\FilesystemHelper::delete_file( $temp_backup );
		}

		$result['bytes_saved'] = max( 0, $original_size - $new_size );

		return $result;
	}
	
	/**
	 * Optimize with the best available tool
	 *
	 * @param string $file_path File path.
	 * @param string $mime_type MIME type.
	 * @param array  $options   Options.
	 * @return array
	 */
	private function optimize_with_best_tool( string $file_path, string $mime_type, array $options ): array {
		$extension = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) );

		if ( 'png' === $extension && ! empty( $options['png_pipeline'] ) ) {
			return $this->optimize_png_with_pipeline( $file_path, $mime_type, $options );
		}
		
		// Get tools that support this format, sorted by priority
		$compatible_tools = [];
		foreach ( $this->available_tools as $tool => $info ) {
			if ( in_array( $extension, $info['formats'], true ) ) {
				if ( 'png' === $extension && empty( $options['png_pipeline'] ) && in_array( $tool, [ 'pngquant', 'oxipng', 'ect' ], true ) ) {
					continue;
				}
				$compatible_tools[ $tool ] = $info['priority'];
			}
		}
		arsort( $compatible_tools );
		
		// Try each tool in order of priority
		foreach ( array_keys( $compatible_tools ) as $tool ) {
			if ( 'cjpegli' === $tool && empty( $options['jpegli_enabled'] ) ) {
				continue;
			}
			$result = $this->optimize_with_tool( $tool, $file_path, $mime_type, $options );
			if ( $result['success'] ) {
				return $result;
			}
		}
		
		return [
			'success' => false,
			'error' => __( 'No optimization tool available', 'prorank-seo' ),
		];
	}

	/**
	 * Run the configured PNG pipeline in sequence.
	 *
	 * @param string $file_path File path.
	 * @param string $mime_type MIME type.
	 * @param array  $options   Options.
	 * @return array
	 */
	private function optimize_png_with_pipeline( string $file_path, string $mime_type, array $options ): array {
		return $this->optimize_with_best_tool(
			$file_path,
			$mime_type,
			array_merge(
				$options,
				[
					'png_pipeline' => false,
				]
			)
		);
	}
	
	/**
	 * Optimize with specific tool
	 *
	 * @param string $tool      Tool name.
	 * @param string $file_path File path.
	 * @param string $mime_type MIME type.
	 * @param array  $options   Options.
	 * @return array
	 */
	private function optimize_with_tool( string $tool, string $file_path, string $mime_type, array $options ): array {
		switch ( $tool ) {
			case 'cjpegli':
				return $this->shell_tool_unavailable();

			case 'pngquant':
				return $this->shell_tool_unavailable();

			case 'jpegoptim':
				return $this->shell_tool_unavailable();

			case 'optipng':
				return $this->shell_tool_unavailable();

			case 'oxipng':
				return $this->shell_tool_unavailable();

			case 'ect':
				return $this->shell_tool_unavailable();

			case 'imagick':
				return $this->optimize_with_imagick( $file_path, $mime_type, $options );
				
			case 'gd':
				return $this->optimize_with_gd( $file_path, $mime_type, $options );
				
			default:
				return [
					'success' => false,
					'error' => __( 'Unknown optimization tool', 'prorank-seo' ),
				];
		}
	}

	/**
	 * Shell-based optimization tools are not used in the free build.
	 *
	 * @return array
	 */
	private function shell_tool_unavailable(): array {
		return [
			'success' => false,
			'error'   => __( 'This optimization method is unavailable in the free plugin build.', 'prorank-seo' ),
		];
	}

	/**
	 * Optimize JPEG with cjpegli (JPEGli).
	 *
	 * @param string $file_path File path.
	 * @param array  $options   Options.
	 * @return array
	 */
	private function optimize_with_cjpegli( string $file_path, array $options ): array {
		return $this->shell_tool_unavailable();
	}
	
	/**
	 * Optimize PNG with pngquant
	 *
	 * @param string $file_path File path.
	 * @param array  $options   Options.
	 * @return array
	 */
	private function optimize_with_pngquant( string $file_path, array $options ): array {
		return $this->shell_tool_unavailable();
	}
	
	/**
	 * Optimize JPEG with jpegoptim
	 *
	 * @param string $file_path File path.
	 * @param array  $options   Options.
	 * @return array
	 */
	private function optimize_with_jpegoptim( string $file_path, array $options ): array {
		return $this->shell_tool_unavailable();
	}
	
	/**
	 * Optimize PNG with optipng
	 *
	 * @param string $file_path File path.
	 * @param array  $options   Options.
	 * @return array
	 */
	private function optimize_with_optipng( string $file_path, array $options ): array {
		return $this->shell_tool_unavailable();
	}

	/**
	 * Optimize PNG with oxipng
	 *
	 * @param string $file_path File path.
	 * @param array  $options   Options.
	 * @return array
	 */
	private function optimize_with_oxipng( string $file_path, array $options ): array {
		return $this->shell_tool_unavailable();
	}

	/**
	 * Optimize PNG with ECT (Efficient Compression Tool)
	 *
	 * ECT provides the final squeeze on PNG files after pngquant/oxipng.
	 * Uses DEFLATE recompression for additional size reduction.
	 *
	 * @param string $file_path File path.
	 * @param array  $options   Options.
	 * @return array
	 */
	private function optimize_with_ect( string $file_path, array $options ): array {
		return $this->shell_tool_unavailable();
	}

	/**
	 * Optimize with ImageMagick
	 *
	 * @param string $file_path File path.
	 * @param string $mime_type MIME type.
	 * @param array  $options   Options.
	 * @return array
	 */
	private function optimize_with_imagick( string $file_path, string $mime_type, array $options ): array {
		try {
			$imagick = new \Imagick( $file_path );
			
			// Strip metadata
			$imagick->stripImage();
			
			// Set compression based on type
			if ( strpos( $mime_type, 'jpeg' ) !== false ) {
				$quality = $this->get_quality_for_compression_type( $options['compression_type'], 'jpeg' );
				$imagick->setImageCompression( \Imagick::COMPRESSION_JPEG );
				$imagick->setImageCompressionQuality( $quality );
				
				// Progressive JPEG
				$imagick->setInterlaceScheme( \Imagick::INTERLACE_PLANE );
			} elseif ( strpos( $mime_type, 'png' ) !== false ) {
				$png_quality       = $this->get_png_quality( $options );
				$compression_level = $this->get_png_compression_level( $png_quality );
				$imagick->setImageCompression( \Imagick::COMPRESSION_ZIP );
				$imagick->setOption( 'png:compression-level', (string) $compression_level );
			}
			
			// Smart compression adjustments
			if ( $options['smart_compression'] ) {
				$this->apply_smart_compression( $imagick );
			}
			
			$imagick->writeImage( $file_path );
			$imagick->destroy();
			
			return [
				'success' => true,
				'method' => 'imagick',
			];
		} catch ( \Exception $e ) {
			return [
				'success' => false,
				'method' => 'imagick',
				'error' => $e->getMessage(),
			];
		}
	}
	
	/**
	 * Optimize with GD
	 *
	 * @param string $file_path File path.
	 * @param string $mime_type MIME type.
	 * @param array  $options   Options.
	 * @return array
	 */
	private function optimize_with_gd( string $file_path, string $mime_type, array $options ): array {
		$image = null;
		
		// Load image
		switch ( $mime_type ) {
			case 'image/jpeg':
				$image = imagecreatefromjpeg( $file_path );
				break;
			case 'image/png':
				$image = imagecreatefrompng( $file_path );
				break;
			case 'image/gif':
				$image = imagecreatefromgif( $file_path );
				break;
			case 'image/webp':
				if ( function_exists( 'imagecreatefromwebp' ) ) {
					$image = imagecreatefromwebp( $file_path );
				}
				break;
		}
		
		if ( ! $image ) {
			return [
				'success' => false,
				'method' => 'gd',
				'error' => __( 'Failed to load image', 'prorank-seo' ),
			];
		}
		
		// Save optimized image
		$success = false;
		switch ( $mime_type ) {
			case 'image/jpeg':
				$quality = $this->get_quality_for_compression_type( $options['compression_type'], 'jpeg' );
				imageinterlace( $image, true ); // Progressive JPEG
				$success = imagejpeg( $image, $file_path, $quality );
				break;
			case 'image/png':
				imagesavealpha( $image, true );
				$success = imagepng( $image, $file_path, $this->get_png_compression_level( $this->get_png_quality( $options ) ) );
				break;
			case 'image/gif':
				$success = imagegif( $image, $file_path );
				break;
			case 'image/webp':
				if ( function_exists( 'imagewebp' ) ) {
					$quality = $this->get_quality_for_compression_type( $options['compression_type'], 'webp' );
					$success = imagewebp( $image, $file_path, $quality );
				}
				break;
		}
		
		imagedestroy( $image );
		
		return [
			'success' => $success,
			'method' => 'gd',
			'error' => ! $success ? __( 'Failed to save image', 'prorank-seo' ) : null,
		];
	}
	
	/**
	 * Get quality setting for compression type
	 *
	 * @param string $compression_type Compression type.
	 * @param string $format           Image format.
	 * @return int
	 */
	private function get_quality_for_compression_type( string $compression_type, string $format ): int {
		$settings = $this->plugin->settings();
		
		// Get base quality from settings
		$base_quality = (int) $settings->get( 'images_compression_level', 85 );
		
		// Adjust based on compression type
		switch ( $compression_type ) {
			case 'lossy':
				return max( 60, $base_quality - 15 );
			case 'glossy':
				return $base_quality;
			case 'lossless':
				return 100;
			default:
				return $base_quality;
		}
	}

	/**
	 * Get the effective PNG quality setting.
	 *
	 * @param array $options Optimization options.
	 * @return int
	 */
	private function get_png_quality( array $options ): int {
		if ( isset( $options['png_quality'] ) ) {
			return max( 50, min( 100, (int) $options['png_quality'] ) );
		}

		return max( 50, min( 100, $this->get_quality_for_compression_type( $options['compression_type'] ?? 'glossy', 'png' ) ) );
	}

	/**
	 * Convert PNG quality to a lossless compression level.
	 *
	 * Higher quality means lighter compression and larger files.
	 *
	 * @param int $quality PNG quality value.
	 * @return int
	 */
	private function get_png_compression_level( int $quality ): int {
		$quality = max( 50, min( 100, $quality ) );

		return max( 0, min( 9, (int) round( ( 100 - $quality ) / 10 ) ) );
	}
	
	/**
	 * Apply smart compression adjustments
	 *
	 * @param \Imagick $imagick Imagick instance.
	 */
	private function apply_smart_compression( \Imagick $imagick ): void {
		// Analyze image characteristics
		$width = $imagick->getImageWidth();
		$height = $imagick->getImageHeight();
		$colors = $imagick->getImageColors();
		
		// High resolution images can use more compression
		if ( $width > 2000 || $height > 2000 ) {
			$current_quality = $imagick->getImageCompressionQuality();
			$imagick->setImageCompressionQuality( max( 70, $current_quality - 10 ) );
		}
		
		// Images with few colors (logos, graphics) need less compression
		if ( $colors < 256 ) {
			$current_quality = $imagick->getImageCompressionQuality();
			$imagick->setImageCompressionQuality( min( 95, $current_quality + 10 ) );
		}
	}
	
	/**
	 * Handle modern format conversion
	 *
	 * @param int    $attachment_id Attachment ID.
	 * @param string $file_path     Original file path.
	 */
	private function handle_modern_formats( int $attachment_id, string $file_path ): void {
		$settings = $this->plugin->settings();

		if ( $settings->get( 'images_convert_to_webp', false ) ) {
			$this->convert_to_webp( $attachment_id, $file_path );
		}

		if ( $settings->get( 'images_convert_to_avif', false ) ) {
			$this->convert_to_avif( $attachment_id, $file_path );
		}

		if ( $settings->get( 'images_convert_to_jxl', false ) ) {
			$this->convert_to_jxl( $attachment_id, $file_path );
		}
	}

	/**
	 * Convert image to JPEG XL
	 *
	 * @param int    $attachment_id Attachment ID.
	 * @param string $source_path   Source file path.
	 * @return array
	 */
	public function convert_to_jxl( int $attachment_id, string $source_path ): array {
		$jxl_path = preg_replace( '/\.(jpe?g|png|gif)$/i', '.jxl', $source_path );
		$settings = $this->plugin->settings();
		$quality = (int) $settings->get( 'images_jxl_quality', 85 );

		if ( isset( $this->available_tools['imagick'] ) && in_array( 'jxl', $this->available_tools['imagick']['formats'], true ) ) {
			try {
				$imagick = new \Imagick( $source_path );
				$imagick->setImageFormat( 'jxl' );
				$imagick->setImageCompressionQuality( $quality );
				$imagick->writeImage( $jxl_path );
				$imagick->destroy();
				$this->register_jxl_file( $attachment_id, $jxl_path );
				return [
					'success'  => true,
					'jxl_path' => $jxl_path,
					'method'   => 'imagick',
				];
			} catch ( \Exception $e ) {
				// Continue to unsupported payload below.
			}
		}

		return [
			'success' => false,
			'error' => __( 'JPEG XL conversion is not available on this server.', 'prorank-seo' ),
		];
	}

	/**
	 * Register JXL file
	 *
	 * @param int    $attachment_id Attachment ID.
	 * @param string $jxl_path      JXL file path.
	 */
	private function register_jxl_file( int $attachment_id, string $jxl_path ): void {
		update_post_meta( $attachment_id, '_prorank_jxl_path', $jxl_path );
		update_post_meta( $attachment_id, '_prorank_jxl_size', filesize( $jxl_path ) );
	}

	/**
	 * Convert image via ProRank Cloud API
	 *
	 * @param string $source_path      Source file path.
	 * @param string $format           Target format (webp, avif, jxl, jpeg, jpegli).
	 * @param int    $quality          Quality level (1-100).
	 * @param string $compression_type Compression type: lossy, glossy, lossless.
	 * @param bool   $smart_compression Whether to enable smart (adaptive) compression.
	 * @return array
	 */
	public function convert_via_cloud( string $source_path, string $format, int $quality = 85, string $compression_type = 'lossy', bool $smart_compression = true ): array {
		$settings = $this->plugin->settings();
		$api_url = defined( 'PRORANK_IMAGE_OPTIMIZE_ENDPOINT' )
			? PRORANK_IMAGE_OPTIMIZE_ENDPOINT
			: 'https://prorank.io/api/v1/optimize';

			if ( $api_url === '#disabled-for-wporg#' || $api_url === '' ) {
				return [
					'success' => false,
					'error' => __( 'Cloud image optimization is not available in this build.', 'prorank-seo' ),
				];
			}

			if ( ! file_exists( $source_path ) || ! is_readable( $source_path ) ) {
				return [
					'success' => false,
					'error' => __( 'Failed to read source file', 'prorank-seo' ),
				];
			}

		return [
			'success' => false,
			'error' => __( 'Cloud image optimization is not available in the free plugin.', 'prorank-seo' ),
		];

		// Check cloud credit quota before making the API request
		try {
			$module = $this->plugin->modules()->get_module( 'image_optimization' );
			if ( $module && method_exists( $module, 'get_quota_status' ) ) {
				$quota_status = $module->get_quota_status();
				if ( ( $quota_status['status'] ?? '' ) !== 'active' ) {
					return [
						'success' => false,
						'error' => $quota_status['message'] ?? __( 'Cloud credits are not available', 'prorank-seo' ),
					];
				}
			}
		} catch ( \Throwable $e ) {
			// Ignore quota check failures and fall back to API response
		}

				$site_url = home_url();
				$site_domain = wp_parse_url( $site_url, PHP_URL_HOST ) ?: '';
				$request_headers = [
					'Authorization' => 'Bearer ' . $auth_token,
					'X-Site-Url' => $site_url,
					'X-Site-Domain' => $site_domain,
				];
			$request_params = [
				'format' => $format,
				'quality' => (string) $quality,
				'compression_type' => $compression_type,
				'smart_compression' => $smart_compression ? '1' : '0',
			];

			$request_result = $this->send_cloud_multipart_request( $api_url, $source_path, $request_headers, $request_params, 60 );
			if ( ! $request_result['ok'] ) {
				return [
					'success' => false,
					'error' => $request_result['error'] ?: __( 'Cloud API request failed', 'prorank-seo' ),
				];
			}

			$response_code = (int) $request_result['status_code'];
			if ( $response_code !== 200 ) {
				$body = json_decode( $request_result['body'], true );
				return [
					'success' => false,
					'error' => $body['message'] ?? __( 'Cloud API request failed', 'prorank-seo' ),
				];
			}

			// Save the optimized file
			$output_path = preg_replace( '/\.(jpe?g|png|gif)$/i', '.' . $format, $source_path );
			$optimized_data = $request_result['body'];

		if ( file_put_contents( $output_path, $optimized_data ) === false ) {
			return [
				'success' => false,
				'error' => __( 'Failed to save optimized file', 'prorank-seo' ),
			];
		}

		return [
			'success' => true,
			'output_path' => $output_path,
			'method' => 'cloud',
			];
		}

	/**
	 * Send multipart cloud request using streaming when available.
	 *
	 * @param string $api_url     API URL.
	 * @param string $source_path Source file path.
	 * @param array  $headers     Request headers.
	 * @param array  $params      Multipart fields.
	 * @param int    $timeout     Request timeout.
	 * @return array
	 */
	private function send_cloud_multipart_request( string $api_url, string $source_path, array $headers, array $params, int $timeout ): array {
		return $this->send_cloud_multipart_request_via_wp( $api_url, $source_path, $headers, $params, $timeout );
	}

	/**
	 * Send multipart request via cURL with file streaming.
	 *
	 * @param string $api_url     API URL.
	 * @param string $source_path Source file path.
	 * @param array  $headers     Request headers.
	 * @param array  $params      Multipart fields.
	 * @param int    $timeout     Request timeout.
	 * @return array
	 */
	private function send_cloud_multipart_request_via_curl( string $api_url, string $source_path, array $headers, array $params, int $timeout ): array {
		return $this->send_cloud_multipart_request_via_wp( $api_url, $source_path, $headers, $params, $timeout );
	}

	/**
	 * Send multipart request via WP HTTP API (fallback for smaller files).
	 *
	 * @param string $api_url     API URL.
	 * @param string $source_path Source file path.
	 * @param array  $headers     Request headers.
	 * @param array  $params      Multipart fields.
	 * @param int    $timeout     Request timeout.
	 * @return array
	 */
	private function send_cloud_multipart_request_via_wp( string $api_url, string $source_path, array $headers, array $params, int $timeout ): array {
		$file_data = file_get_contents( $source_path );
		if ( false === $file_data ) {
			return [
				'ok' => false,
				'status_code' => 0,
				'body' => '',
				'headers' => [],
				'error' => __( 'Failed to read source file', 'prorank-seo' ),
			];
		}

		$boundary = wp_generate_password( 24, false );
		$body = '';

		$body .= "--{$boundary}\r\n";
		$body .= "Content-Disposition: form-data; name=\"image\"; filename=\"" . basename( $source_path ) . "\"\r\n";
		$body .= "Content-Type: " . ( wp_check_filetype( $source_path )['type'] ?: 'application/octet-stream' ) . "\r\n\r\n";
		$body .= $file_data . "\r\n";

		foreach ( $params as $key => $value ) {
			$body .= "--{$boundary}\r\n";
			$body .= "Content-Disposition: form-data; name=\"{$key}\"\r\n\r\n";
			$body .= $value . "\r\n";
		}

		$body .= "--{$boundary}--\r\n";

		$request_headers = $headers;
		$request_headers['Content-Type'] = 'multipart/form-data; boundary=' . $boundary;

		$response = wp_remote_post(
			$api_url,
			[
				'timeout' => $timeout,
				'headers' => $request_headers,
				'body' => $body,
			]
		);

		if ( is_wp_error( $response ) ) {
			return [
				'ok' => false,
				'status_code' => 0,
				'body' => '',
				'headers' => [],
				'error' => $response->get_error_message(),
			];
		}

		$normalized_headers = [];
		$raw_headers = wp_remote_retrieve_headers( $response );
		if ( is_array( $raw_headers ) || $raw_headers instanceof \Traversable ) {
			foreach ( $raw_headers as $key => $value ) {
				$normalized_headers[ strtolower( (string) $key ) ] = is_array( $value ) ? implode( ', ', $value ) : (string) $value;
			}
		}

		return [
			'ok' => true,
			'status_code' => (int) wp_remote_retrieve_response_code( $response ),
			'body' => (string) wp_remote_retrieve_body( $response ),
			'headers' => $normalized_headers,
			'error' => '',
		];
	}

	/**
	 * Determine if cloud upload should use streaming transport.
	 *
	 * @param string $source_path Source file path.
	 * @return bool
	 */
	private function should_stream_cloud_upload( string $source_path ): bool {
		$threshold = (int) apply_filters( 'prorank_cloud_streaming_threshold_bytes', 5 * 1024 * 1024, $source_path );
		$file_size = @filesize( $source_path );
		if ( false === $file_size ) {
			return false;
		}

		return $file_size >= max( 0, $threshold );
	}
		
	/**
	 * Convert image to WebP
	 *
	 * @param int    $attachment_id Attachment ID.
	 * @param string $source_path   Source file path.
	 * @return array
	 */
	public function convert_to_webp( int $attachment_id, string $source_path ): array {
		$webp_path = preg_replace( '/\.(jpe?g|png)$/i', '.webp', $source_path );
		
		// Fallback to ImageMagick
		if ( isset( $this->available_tools['imagick'] ) ) {
			try {
				$imagick = new \Imagick( $source_path );
				$imagick->setImageFormat( 'webp' );
				$imagick->setImageCompressionQuality( $this->get_quality_for_compression_type( 'glossy', 'webp' ) );
				$imagick->writeImage( $webp_path );
				$imagick->destroy();
				
				$this->register_webp_file( $attachment_id, $webp_path );
				return [
					'success' => true,
					'webp_path' => $webp_path,
					'method' => 'imagick',
				];
			} catch ( \Exception $e ) {
				// Continue to next method
			}
		}
		
		// Fallback to GD
		if ( function_exists( 'imagewebp' ) ) {
			$image = null;
			$mime_type = wp_check_filetype( $source_path )['type'];
			
			switch ( $mime_type ) {
				case 'image/jpeg':
					$image = imagecreatefromjpeg( $source_path );
					break;
				case 'image/png':
					$image = imagecreatefrompng( $source_path );
					imagesavealpha( $image, true );
					break;
			}
			
			if ( $image ) {
				$quality = $this->get_quality_for_compression_type( 'glossy', 'webp' );
				if ( imagewebp( $image, $webp_path, $quality ) ) {
					imagedestroy( $image );
					$this->register_webp_file( $attachment_id, $webp_path );
					return [
						'success' => true,
						'webp_path' => $webp_path,
						'method' => 'gd',
					];
				}
				imagedestroy( $image );
			}
		}
		
		return [
			'success' => false,
			'error' => __( 'WebP conversion failed', 'prorank-seo' ),
		];
	}
	
	/**
	 * Convert image to AVIF
	 *
	 * @param int    $attachment_id Attachment ID.
	 * @param string $source_path   Source file path.
	 * @return array
	 */
	public function convert_to_avif( int $attachment_id, string $source_path ): array {
		$avif_path = preg_replace( '/\.(jpe?g|png)$/i', '.avif', $source_path );
		$settings = $this->plugin->settings();
		$compression_type = $settings->get( 'image_optimization_compression_type', 'lossy' );

		// Try ImageMagick (if it supports AVIF)
		if ( isset( $this->available_tools['imagick'] ) && in_array( 'avif', $this->available_tools['imagick']['formats'], true ) ) {
			try {
				$imagick = new \Imagick( $source_path );
				$imagick->setImageFormat( 'avif' );
				$imagick->setImageCompressionQuality( $this->get_quality_for_compression_type( $compression_type, 'avif' ) );
				$imagick->writeImage( $avif_path );
				$imagick->destroy();

					// Discard if AVIF is larger than source
					if ( file_exists( $avif_path ) && filesize( $avif_path ) >= filesize( $source_path ) ) {
						\ProRank\SEO\Core\FilesystemHelper::delete_file( $avif_path );
						return [
							'success' => false,
							'error' => __( 'AVIF larger than source — skipped', 'prorank-seo' ),
					];
				}

				$this->register_avif_file( $attachment_id, $avif_path );
				return [
					'success' => true,
					'avif_path' => $avif_path,
					'method' => 'imagick',
				];
			} catch ( \Exception $e ) {
				// AVIF not supported
			}
		}

		return [
			'success' => false,
			'error' => __( 'AVIF conversion not available', 'prorank-seo' ),
		];
	}
	
	/**
	 * Backup original file
	 *
	 * @param string $file_path File path.
	 */
	private function backup_original( string $file_path ): void {
		$backup_dir = wp_upload_dir()['basedir'] . '/prorank-seo-backups';
		if ( ! file_exists( $backup_dir ) ) {
			wp_mkdir_p( $backup_dir );
		}
		
		$backup_path = $backup_dir . '/' . basename( $file_path ) . '.original';
		if ( ! file_exists( $backup_path ) ) {
			copy( $file_path, $backup_path );
		}
	}
	
	/**
	 * Register WebP file
	 *
	 * @param int    $attachment_id Attachment ID.
	 * @param string $webp_path     WebP file path.
	 */
	private function register_webp_file( int $attachment_id, string $webp_path ): void {
		update_post_meta( $attachment_id, '_prorank_webp_path', $webp_path );
		update_post_meta( $attachment_id, '_prorank_webp_size', filesize( $webp_path ) );
	}
	
	/**
	 * Register AVIF file
	 *
	 * @param int    $attachment_id Attachment ID.
	 * @param string $avif_path     AVIF file path.
	 */
	private function register_avif_file( int $attachment_id, string $avif_path ): void {
		update_post_meta( $attachment_id, '_prorank_avif_path', $avif_path );
		update_post_meta( $attachment_id, '_prorank_avif_size', filesize( $avif_path ) );
	}
	
	/**
	 * Get optimization capabilities
	 *
	 * @return array
	 */
	public function get_capabilities(): array {
		$settings = $this->plugin->settings();
		// Read from image optimization settings option
		$image_settings = get_option( 'prorank_image_optimization_settings', [] );
		$optimization_method = $this->normalize_optimization_method( $image_settings['optimization_method'] ?? 'local' );
		$cloud_available = $optimization_method === 'cloud' || $optimization_method === 'prorank' || $optimization_method === 'smart';

		return [
			'tools' => $this->available_tools,
			'formats' => [
				'webp' => isset( $this->available_tools['cwebp'] ) || isset( $this->available_tools['imagick'] ) || function_exists( 'imagewebp' ) || $cloud_available,
				'avif' => isset( $this->available_tools['avifenc'] ) || ( isset( $this->available_tools['imagick'] ) && in_array( 'avif', $this->available_tools['imagick']['formats'], true ) ) || $cloud_available,
				'jxl' => isset( $this->available_tools['cjxl'] ) || $cloud_available,
				'jpegli' => isset( $this->available_tools['cjpegli'] ) || $cloud_available,
			],
			'features' => [
				'smart_compression' => true,
				'batch_processing' => true,
				'background_processing' => true,
				'auto_webp' => true,
				'auto_avif' => true,
				'auto_jxl' => isset( $this->available_tools['cjxl'] ) || $cloud_available,
				'jpegli_encoding' => isset( $this->available_tools['cjpegli'] ) || $cloud_available,
				'progressive_jpeg' => true,
				'strip_metadata' => true,
				'backup_originals' => true,
				'cloud_optimization' => $cloud_available,
			],
			'cloud' => [
				'available' => $cloud_available,
				'formats' => [ 'webp', 'avif', 'jxl', 'jpeg' ],
				'version' => 'Dec 2025 Stack',
			],
		];
	}
}
