<?php

/**
 * WebP/AVIF Delivery System
 *
 * Automatically serves modern image formats to supported browsers
 *
 * @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;
}


/**
 * WebPDelivery class
 */


class WebPDelivery {
	
	/**
	 * Plugin instance
	 *
	 * @var Plugin
	 */
	private Plugin $plugin;
	
	/**
	 * Constructor
	 */
	public function __construct() {
		$this->plugin = Plugin::get_instance();
		$this->init();
	}
	
	/**
	 * Initialize WebP delivery
	 */
	private function init(): void {
		$settings = $this->plugin->settings();
		$delivery_method = $settings->get( 'images_delivery_method', 'picture' );
		
		if ( $delivery_method === 'none' ) {
			return;
		}
		
		// Picture element method (recommended)
		if ( in_array( $delivery_method, [ 'picture', 'both' ], true ) ) {
			add_filter( 'the_content', [ $this, 'wrap_images_in_picture' ], 20 );
			add_filter( 'post_thumbnail_html', [ $this, 'wrap_featured_image_in_picture' ], 20 );
			add_filter( 'wp_get_attachment_image', [ $this, 'wrap_attachment_image_in_picture' ], 20, 5 );
		}
		
		// Rewrite method (server-side)
		if ( in_array( $delivery_method, [ 'rewrite', 'both' ], true ) ) {
			add_action( 'init', [ $this, 'add_rewrite_rules' ] );
			add_filter( 'mod_rewrite_rules', [ $this, 'add_htaccess_rules' ] );
		}
		
		// Add support for srcset
		add_filter( 'wp_calculate_image_srcset', [ $this, 'add_webp_to_srcset' ], 10, 5 );
		
		// Preload modern formats
		add_action( 'wp_head', [ $this, 'preload_modern_formats' ], 1 );
	}
	
	/**
	 * Wrap images in picture elements
	 *
	 * @param string $content Content.
	 * @return string
	 */
	public function wrap_images_in_picture( string $content ): string {
		if ( ! $this->should_process() ) {
			return $content;
		}
		
		// Find all img tags
		$pattern = '/<img([^>]+)>/i';
		
		return preg_replace_callback( $pattern, [ $this, 'convert_img_to_picture' ], $content );
	}
	
	/**
	 * Convert img tag to picture element
	 *
	 * @param array $matches Regex matches.
	 * @return string
	 */
	private function convert_img_to_picture( array $matches ): string {
		$img_tag = $matches[0];
		$attributes = $matches[1];
		
		// Extract src
		if ( ! preg_match( '/src=["\']([^"\']+)["\']/', $attributes, $src_match ) ) {
			return $img_tag;
		}
		
		$original_src = $src_match[1];
		
		// Skip if already WebP/AVIF
		if ( preg_match( '/\.(webp|avif)$/i', $original_src ) ) {
			return $img_tag;
		}
		
		// Get attachment ID from URL
		$attachment_id = $this->get_attachment_id_from_url( $original_src );
		if ( ! $attachment_id ) {
			return $img_tag;
		}
		
		// Check for modern format versions
		$webp_path = get_post_meta( $attachment_id, '_prorank_webp_path', true );
		$avif_path = get_post_meta( $attachment_id, '_prorank_avif_path', true );
		
		if ( ! $webp_path && ! $avif_path ) {
			return $img_tag;
		}
		
		// Build picture element
		$picture = '<picture>';
		
		// Add AVIF source if available
		if ( $avif_path && file_exists( $avif_path ) ) {
			$avif_url = $this->path_to_url( $avif_path );
			$picture .= sprintf(
				'<source srcset="%s" type="image/avif">',
				esc_url( $avif_url )
			);
		}
		
		// Add WebP source if available
		if ( $webp_path && file_exists( $webp_path ) ) {
			$webp_url = $this->path_to_url( $webp_path );
			$picture .= sprintf(
				'<source srcset="%s" type="image/webp">',
				esc_url( $webp_url )
			);
		}
		
		// Add original image as fallback
		$picture .= $img_tag;
		$picture .= '</picture>';
		
		return $picture;
	}
	
	/**
	 * Wrap featured image in picture element
	 *
	 * @param string $html Featured image HTML.
	 * @return string
	 */
	public function wrap_featured_image_in_picture( string $html ): string {
		return $this->wrap_images_in_picture( $html );
	}
	
	/**
	 * Wrap attachment image in picture element
	 *
	 * @param string $html          Image HTML.
	 * @param int    $attachment_id Attachment ID.
	 * @param string $size          Image size.
	 * @param bool   $icon          Whether it's an icon.
	 * @param array  $attr          Attributes.
	 * @return string
	 */
	public function wrap_attachment_image_in_picture( string $html, int $attachment_id, string $size, bool $icon, array $attr ): string {
		if ( ! $this->should_process() ) {
			return $html;
		}
		
		// Check for modern format versions
		$webp_path = get_post_meta( $attachment_id, '_prorank_webp_path', true );
		$avif_path = get_post_meta( $attachment_id, '_prorank_avif_path', true );
		
		if ( ! $webp_path && ! $avif_path ) {
			return $html;
		}
		
		// Build picture element
		$picture = '<picture>';
		
		// Add AVIF source if available
		if ( $avif_path && file_exists( $avif_path ) ) {
			$avif_url = $this->path_to_url( $avif_path );
			$picture .= sprintf(
				'<source srcset="%s" type="image/avif">',
				esc_url( $avif_url )
			);
		}
		
		// Add WebP source if available
		if ( $webp_path && file_exists( $webp_path ) ) {
			$webp_url = $this->path_to_url( $webp_path );
			$picture .= sprintf(
				'<source srcset="%s" type="image/webp">',
				esc_url( $webp_url )
			);
		}
		
		// Add original image
		$picture .= $html;
		$picture .= '</picture>';
		
		return $picture;
	}
	
	/**
	 * Add WebP versions to srcset
	 *
	 * @param array  $sources       Sources array.
	 * @param array  $size_array    Size array.
	 * @param string $image_src     Image source.
	 * @param array  $image_meta    Image metadata.
	 * @param int    $attachment_id Attachment ID.
	 * @return array
	 */
	public function add_webp_to_srcset( array $sources, array $size_array, string $image_src, array $image_meta, int $attachment_id ): array {
		// Check if browser supports WebP
		if ( ! $this->browser_supports_webp() ) {
			return $sources;
		}
		
		$webp_path = get_post_meta( $attachment_id, '_prorank_webp_path', true );
		if ( ! $webp_path || ! file_exists( $webp_path ) ) {
			return $sources;
		}
		
		// Add WebP version to sources
		$webp_url = $this->path_to_url( $webp_path );
		$sources['webp'] = [
			'url' => $webp_url,
			'descriptor' => 'w',
			'value' => $size_array[0],
		];
		
		return $sources;
	}
	
	/**
	 * Add rewrite rules for WebP
	 */
	public function add_rewrite_rules(): void {
		add_rewrite_rule(
			'^wp-content/uploads/(.+)\.(jpe?g|png)$',
			'index.php?prorank_webp_image=$matches[1].$matches[2]',
			'top'
		);
	}
	
	/**
	 * Add htaccess rules for WebP
	 *
	 * @param string $rules Current rules.
	 * @return string
	 */
	public function add_htaccess_rules( string $rules ): string {
        $webp_rules = "\n# BEGIN ProRank SEO WebP\n";
        $webp_rules .= "<IfModule mod_rewrite.c>\n";
        $webp_rules .= "RewriteEngine On\n\n";
        $webp_rules .= "# Check if browser supports WebP\n";
        $webp_rules .= "RewriteCond %{HTTP_ACCEPT} image/webp\n\n";
        $webp_rules .= "# Check if WebP version exists\n";
        $webp_rules .= "RewriteCond %{REQUEST_FILENAME}.webp -f\n\n";
        $webp_rules .= "# Serve WebP version\n";
        $webp_rules .= "RewriteRule ^(.+)\\.(jpe?g|png)$ $1.$2.webp [T=image/webp,E=accept:1]\n";
        $webp_rules .= "</IfModule>\n\n";
        $webp_rules .= "<IfModule mod_headers.c>\n";
        $webp_rules .= "Header append Vary Accept env=REDIRECT_accept\n";
        $webp_rules .= "</IfModule>\n\n";
        $webp_rules .= "<IfModule mod_mime.c>\n";
        $webp_rules .= "AddType image/webp .webp\n";
        $webp_rules .= "</IfModule>\n";
        $webp_rules .= "# END ProRank SEO WebP\n";

		return $webp_rules . $rules;
	}
	
	/**
	 * Preload modern format images
	 */
	public function preload_modern_formats(): void {
		if ( ! is_singular() ) {
			return;
		}
		
		$post_id = get_the_ID();
		$thumbnail_id = get_post_thumbnail_id( $post_id );
		
		if ( ! $thumbnail_id ) {
			return;
		}
		
		// Check for WebP version
		$webp_path = get_post_meta( $thumbnail_id, '_prorank_webp_path', true );
		if ( $webp_path && file_exists( $webp_path ) ) {
			$webp_url = $this->path_to_url( $webp_path );
			printf(
				'<link rel="preload" as="image" href="%s" type="image/webp">' . "\n",
				esc_url( $webp_url )
			);
		}
		
		// Check for AVIF version
		$avif_path = get_post_meta( $thumbnail_id, '_prorank_avif_path', true );
		if ( $avif_path && file_exists( $avif_path ) ) {
			$avif_url = $this->path_to_url( $avif_path );
			printf(
				'<link rel="preload" as="image" href="%s" type="image/avif">' . "\n",
				esc_url( $avif_url )
			);
		}
	}
	
	/**
	 * Check if processing should occur
	 *
	 * @return bool
	 */
	private function should_process(): bool {
		// Don't process in admin
		if ( is_admin() ) {
			return false;
		}
		
		// Don't process for REST API requests
		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return false;
		}
		
		// Check if modules are enabled
		$settings = $this->plugin->settings();
		return $settings->get( 'images_convert_to_webp', false ) || 
		       $settings->get( 'images_convert_to_avif', false );
	}
	
	/**
	 * Check if browser supports WebP
	 *
	 * @return bool
	 */
	private function browser_supports_webp(): bool {
		return isset( $_SERVER['HTTP_ACCEPT'] ) && 
		       strpos( $_SERVER['HTTP_ACCEPT'], 'image/webp' ) !== false;
	}
	
	/**
	 * Get attachment ID from URL
	 *
	 * @param string $url Image URL.
	 * @return int
	 */
	private function get_attachment_id_from_url( string $url ): int {
		global $wpdb;
		
		$upload_dir = wp_upload_dir();
		$base_url = $upload_dir['baseurl'];
		
		// Remove base URL
		$file_path = str_replace( $base_url . '/', '', $url );
		
		// Remove size suffix
		$file_path = preg_replace( '/-\d+x\d+(\.[^.]+)$/i', '$1', $file_path );
		
		// Query database
		$attachment_id = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT ID FROM {$wpdb->posts} 
				JOIN {$wpdb->postmeta} ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id 
				WHERE {$wpdb->posts}.post_type = 'attachment' 
				AND {$wpdb->postmeta}.meta_key = '_wp_attached_file' 
				AND {$wpdb->postmeta}.meta_value = %s",
				$file_path
			)
		);
		
		return (int) $attachment_id;
	}
	
	/**
	 * Convert file path to URL
	 *
	 * @param string $path File path.
	 * @return string
	 */
	private function path_to_url( string $path ): string {
		$upload_dir = wp_upload_dir();
		return str_replace( $upload_dir['basedir'], $upload_dir['baseurl'], $path );
	}
}
