<?php
/**
 * WebP converter functionality for STWP plugin.
 *
 * @package STWP
 * @since 1.0.0
 */

namespace STWP;

// If this file is called directly, abort.
if ( ! defined( 'ABSPATH' ) ) {
	die( 'Direct access not allowed.' );
}

/**
 * Converter class handles image conversion to WebP format.
 *
 * @since 1.0.0
 */
final class Converter {

	/**
	 * Initialize converter hooks and actions.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public static function init() {
		\add_filter( 'wp_handle_upload', array( __CLASS__, 'handle_upload' ), 20, 2 );
		\add_action( 'admin_post_stwp_restore_original', array( __CLASS__, 'restore_original_handler' ) );
	}

	/**
	 * Convert uploads to WebP format with safety checks.
	 *
	 * @since 1.0.0
	 * @param array  $upload  Upload data array containing 'file', 'url', 'type'.
	 * @param string $context Upload context.
	 * @return array Modified or original upload data.
	 */
	public static function handle_upload( $upload, $context = '' ) {
		// Only run when conversion is enabled.
		if ( ! Utils::bool_option( 'stwp_convert_to_webp', false ) ) {
			return $upload;
		}

		// Validate upload data.
		if ( empty( $upload['file'] ) || ! \file_exists( $upload['file'] ) || ! \is_readable( $upload['file'] ) ) {
			return $upload;
		}

		// Check WebP support on server.
		if ( ! Utils::can_output_webp() ) {
			Utils::safe_log( 'WebP output not supported on this server. Skipping conversion.' );
			return $upload;
		}

		$mime        = isset( $upload['type'] ) ? $upload['type'] : '';
		$valid_mimes = array( 'image/jpeg', 'image/png', 'image/gif', 'image/heic', 'image/heif' );
		if ( ! \in_array( $mime, $valid_mimes, true ) ) {
			return $upload;
		}

		// Skip animated GIFs to preserve animation.
		if ( 'image/gif' === $mime && Utils::is_animated_gif( $upload['file'] ) ) {
			Utils::safe_log( 'Animated GIF detected, skipping conversion.' );
			return $upload;
		}

		// Get conversion settings.
		$max_w    = Utils::clamp_int( Utils::get_option( 'stwp_max_width', 1920 ), 1, 9999, 1920 );
		$max_h    = Utils::clamp_int( Utils::get_option( 'stwp_max_height', 1080 ), 1, 9999, 1080 );
		$quality  = Utils::clamp_int( Utils::get_option( 'stwp_compression_quality', 80 ), 1, 100, 80 );
		$keep_original = Utils::bool_option( 'stwp_keep_original_backup', true );

		$src = $upload['file'];
		$pi  = \pathinfo( $src );
		$dst = $pi['dirname'] . '/' . $pi['filename'] . '.webp';

		// Prevent file overwrites.
		if ( \file_exists( $dst ) ) {
			$dst = $pi['dirname'] . '/' . $pi['filename'] . '-' . \wp_generate_password( 6, false, false ) . '.webp';
		}

		// Create backup before conversion if requested.
		$backup_path = '';
		if ( $keep_original ) {
			$backup_path = Utils::safe_backup_path( $src );
			if ( ! \file_exists( $backup_path ) ) {
				// Use WP_Filesystem for file operations.
				if ( ! \function_exists( 'WP_Filesystem' ) ) {
					require_once \ABSPATH . 'wp-admin/includes/file.php';
				}
				\WP_Filesystem();
				global $wp_filesystem;
				if ( $wp_filesystem ) {
					$wp_filesystem->copy( $src, $backup_path, false, \FS_CHMOD_FILE );
				}
			}
		}

		try {
			$result = self::convert_to_webp( $src, $dst, $max_w, $max_h, $quality );

			if ( ! $result || ! \file_exists( $dst ) ) {
				Utils::safe_log( 'Conversion failed, keeping original.' );
				if ( $keep_original && \file_exists( $backup_path ) && ! \file_exists( $src ) ) {
					// Restore from backup using WP_Filesystem.
					if ( ! \function_exists( 'WP_Filesystem' ) ) {
						require_once \ABSPATH . 'wp-admin/includes/file.php';
					}
					\WP_Filesystem();
					global $wp_filesystem;
					if ( $wp_filesystem ) {
						$wp_filesystem->copy( $backup_path, $src, false, \FS_CHMOD_FILE );
					}
				}
				// Clean up failed conversion file.
				if ( \file_exists( $dst ) ) {
					\wp_delete_file( $dst );
				}
				return $upload;
			}

			// Compare file sizes, keep WebP only if smaller (unless forced).
			$force     = Utils::bool_option( 'stwp_force_webp', false );
			$orig_size = \filesize( $src );
			$webp_size = \filesize( $dst );

			if ( ! $force && $orig_size && $webp_size && $webp_size >= $orig_size ) {
				Utils::safe_log( 'WebP not smaller (orig=' . $orig_size . ', webp=' . $webp_size . '). Keeping original.' );
				\wp_delete_file( $dst );
				return $upload;
			}

			// Replace upload with WebP version.
			$upload['file'] = $dst;
			$upload['url']  = \str_replace( \basename( $upload['url'] ), \basename( $dst ), $upload['url'] );
			$upload['type'] = 'image/webp';

			// Remove original if not keeping backup.
			if ( ! $keep_original ) {
				\wp_delete_file( $src );
			}

			// Store metadata for restoration.
			self::write_sidecar_meta(
				$dst,
				array(
					'original_path' => $src,
					'backup_path'   => $backup_path,
					'original_mime' => $mime,
					'created_at'    => \time(),
				)
			);

		} catch ( \Exception $e ) {
			Utils::safe_log( 'Exception during conversion: ' . $e->getMessage() );
			\wp_delete_file( $dst );
			return $upload;
		}

		return $upload;
	}

	/**
	 * Convert image to WebP format using WP_Image_Editor.
	 *
	 * @since 1.0.0
	 * @param string $src     Source file path.
	 * @param string $dst     Destination file path.
	 * @param int    $max_w   Maximum width.
	 * @param int    $max_h   Maximum height.
	 * @param int    $quality Compression quality (1-100).
	 * @return bool True if conversion succeeded, false otherwise.
	 */
	private static function convert_to_webp( $src, $dst, $max_w, $max_h, $quality ) {
		// Prefer WP_Image_Editor for best compatibility.
		$editor = \wp_get_image_editor( $src );
		if ( \is_wp_error( $editor ) ) {
			Utils::safe_log( 'wp_get_image_editor error: ' . $editor->get_error_message() );
			return self::gd_fallback_convert( $src, $dst, $max_w, $max_h, $quality );
		}

		// Resize only if needed, preserving aspect ratio.
		$size = $editor->get_size();
		$w    = isset( $size['width'] ) ? (int) $size['width'] : 0;
		$h    = isset( $size['height'] ) ? (int) $size['height'] : 0;

		if ( $w > 0 && $h > 0 && ( $w > $max_w || $h > $max_h ) ) {
			$editor->resize( $max_w, $max_h, false );
		}

		// Set quality and save as WebP.
		$editor->set_quality( $quality );

		$saved = $editor->save( $dst, 'image/webp' );
		if ( \is_wp_error( $saved ) ) {
			Utils::safe_log( 'Editor save error: ' . $saved->get_error_message() );
			return self::gd_fallback_convert( $src, $dst, $max_w, $max_h, $quality );
		}

		return ! empty( $saved['path'] ) && \file_exists( $saved['path'] );
	}

	/**
	 * Fallback GD-based WebP conversion.
	 *
	 * @since 1.0.0
	 * @param string $src     Source file path.
	 * @param string $dst     Destination file path.
	 * @param int    $max_w   Maximum width.
	 * @param int    $max_h   Maximum height.
	 * @param int    $quality Compression quality (1-100).
	 * @return bool True if conversion succeeded, false otherwise.
	 */
	private static function gd_fallback_convert( $src, $dst, $max_w, $max_h, $quality ) {
		if ( ! \function_exists( 'imagewebp' ) ) {
			return false;
		}

		// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
		$info = @\getimagesize( $src );
		if ( ! $info || empty( $info['mime'] ) ) {
			return false;
		}

		$mime   = $info['mime'];
		$create = null;
		if ( 'image/jpeg' === $mime && \function_exists( 'imagecreatefromjpeg' ) ) {
			$create = 'imagecreatefromjpeg';
		}
		if ( 'image/png' === $mime && \function_exists( 'imagecreatefrompng' ) ) {
			$create = 'imagecreatefrompng';
		}
		if ( 'image/gif' === $mime && \function_exists( 'imagecreatefromgif' ) ) {
			$create = 'imagecreatefromgif';
		}
		// HEIC/HEIF not supported via pure GD typically.
		if ( ! $create ) {
			return false;
		}

		// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
		$img = @$create( $src );
		if ( ! $img ) {
			return false;
		}

		$w = \imagesx( $img );
		$h = \imagesy( $img );

		$new_w = $w;
		$new_h = $h;
		if ( $w > $max_w || $h > $max_h ) {
			$ratio = $w / \max( 1, $h );
			$new_w = \min( $max_w, $w );
			$new_h = (int) \round( $new_w / $ratio );
			if ( $new_h > $max_h ) {
				$new_h = \min( $max_h, $h );
				$new_w = (int) \round( $new_h * $ratio );
			}
		}

		$dst_img = \imagecreatetruecolor( $new_w, $new_h );

		// Preserve transparency for PNG/GIF.
		if ( \in_array( $mime, array( 'image/png', 'image/gif' ), true ) ) {
			\imagealphablending( $dst_img, false );
			\imagesavealpha( $dst_img, true );
			$transparent = \imagecolorallocatealpha( $dst_img, 0, 0, 0, 127 );
			\imagefilledrectangle( $dst_img, 0, 0, $new_w, $new_h, $transparent );
		}

		\imagecopyresampled( $dst_img, $img, 0, 0, 0, 0, $new_w, $new_h, $w, $h );

		$ok = \imagewebp( $dst_img, $dst, $quality );

		\imagedestroy( $img );
		\imagedestroy( $dst_img );

		return (bool) $ok && \file_exists( $dst );
	}

	/**
	 * Write sidecar metadata JSON file for restoration.
	 *
	 * @since 1.0.0
	 * @param string $webp_path Path to WebP file.
	 * @param array  $data      Metadata array.
	 * @return void
	 */
	private static function write_sidecar_meta( $webp_path, $data ) {
		$meta_path = $webp_path . '.stwp.json';
		// Use WP_Filesystem for file operations.
		if ( ! \function_exists( 'WP_Filesystem' ) ) {
			require_once \ABSPATH . 'wp-admin/includes/file.php';
		}
		\WP_Filesystem();
		global $wp_filesystem;
		if ( $wp_filesystem ) {
			$wp_filesystem->put_contents( $meta_path, \wp_json_encode( $data ), \FS_CHMOD_FILE );
		}
	}

	/**
	 * Handle restoration of original image from backup.
	 *
	 * @since 1.1.0
	 * @return void
	 */
	public static function restore_original_handler() {
		// Verify nonce first.
		\check_admin_referer( 'stwp_restore_original' );

		// Get and validate attachment ID.
		$attachment_id = isset( $_GET['attachment_id'] ) ? \absint( $_GET['attachment_id'] ) : 0;
		if ( ! $attachment_id ) {
			\wp_die( \esc_html__( 'Invalid attachment ID.', 'stintlief-webp-converter' ), 400 );
		}

		// Verify user can edit this specific attachment.
		if ( ! \current_user_can( 'edit_post', $attachment_id ) ) {
			\wp_die( \esc_html__( 'You do not have permission to edit this attachment.', 'stintlief-webp-converter' ), 403 );
		}

		$file = \get_attached_file( $attachment_id );
		if ( ! $file || ! \file_exists( $file ) ) {
			\wp_safe_redirect( \add_query_arg( array( 'stwp_restored' => 0 ), \admin_url( 'post.php?post=' . $attachment_id . '&action=edit' ) ) );
			exit;
		}

		// Check for metadata file.
		$meta_path = $file . '.stwp.json';
		if ( ! \file_exists( $meta_path ) ) {
			\wp_safe_redirect( \add_query_arg( array( 'stwp_restored' => 0 ), \admin_url( 'post.php?post=' . $attachment_id . '&action=edit' ) ) );
			exit;
		}

		// Use WP_Filesystem for file operations.
		if ( ! \function_exists( 'WP_Filesystem' ) ) {
			require_once \ABSPATH . 'wp-admin/includes/file.php';
		}
		\WP_Filesystem();
		global $wp_filesystem;

		$json_content = '';
		if ( $wp_filesystem ) {
			$json_content = $wp_filesystem->get_contents( $meta_path );
		}

		if ( ! $json_content ) {
			\wp_safe_redirect( \add_query_arg( array( 'stwp_restored' => 0 ), \admin_url( 'post.php?post=' . $attachment_id . '&action=edit' ) ) );
			exit;
		}

		$json          = \json_decode( $json_content, true );
		$backup        = isset( $json['backup_path'] ) ? $json['backup_path'] : '';
		$original_path = isset( $json['original_path'] ) ? $json['original_path'] : '';

		if ( ! $backup || ! \file_exists( $backup ) || ! $original_path ) {
			\wp_safe_redirect( \add_query_arg( array( 'stwp_restored' => 0 ), \admin_url( 'post.php?post=' . $attachment_id . '&action=edit' ) ) );
			exit;
		}

		// Restore file from backup using WP_Filesystem.
		if ( $wp_filesystem ) {
			$wp_filesystem->copy( $backup, $original_path, false, \FS_CHMOD_FILE );
		}

		// Update attachment file path.
		\update_attached_file( $attachment_id, $original_path );

		// Update MIME type.
		$original_mime = isset( $json['original_mime'] ) ? $json['original_mime'] : '';
		if ( $original_mime ) {
			\wp_update_post(
				array(
					'ID'             => $attachment_id,
					'post_mime_type' => $original_mime,
				)
			);
		}

		// Regenerate attachment metadata.
		if ( \function_exists( 'wp_generate_attachment_metadata' ) ) {
			require_once \ABSPATH . 'wp-admin/includes/image.php';
			$metadata = \wp_generate_attachment_metadata( $attachment_id, $original_path );
			if ( \is_array( $metadata ) ) {
				\wp_update_attachment_metadata( $attachment_id, $metadata );
			}
		}

		\wp_safe_redirect( \add_query_arg( array( 'stwp_restored' => 1 ), \admin_url( 'post.php?post=' . $attachment_id . '&action=edit' ) ) );
		exit;
	}
}
