<?php
/**
 * Lazy media optimizer.
 *
 * Adds lazy loading to iframes and decoding="async" to images
 * for improved perceived performance.
 *
 * @package MonitorCat\Modules\Speed\Optimizer
 */

declare(strict_types=1);

namespace MonitorCat\Modules\Speed\Optimizer;

defined( 'ABSPATH' ) || exit;

use MonitorCat\Settings;

/**
 * Optimizes media loading with lazy loading and async decoding.
 */
final class LazyMediaOptimizer {

	/**
	 * Plugin settings.
	 *
	 * @var Settings
	 */
	private Settings $settings;

	/**
	 * Constructor.
	 *
	 * @param Settings $settings Plugin settings.
	 */
	public function __construct( Settings $settings ) {
		$this->settings = $settings;
	}

	/**
	 * Register hooks if the optimization is enabled.
	 */
	public function boot(): void {
		if ( ! $this->settings->get( 'speed.optimize_lazy_media', true ) ) {
			return;
		}

		if ( is_admin() ) {
			return;
		}

		// Add loading="lazy" to iframes in content.
		add_filter( 'the_content', array( $this, 'lazy_load_iframes' ), 99 );

		// Add decoding="async" to images.
		add_filter( 'wp_get_attachment_image_attributes', array( $this, 'add_decode_async' ), 10 );
	}

	/**
	 * Add loading="lazy" attribute to iframes that don't already have it.
	 *
	 * @param string $content The post content.
	 * @return string Modified content.
	 */
	public function lazy_load_iframes( string $content ): string {
		if ( empty( $content ) || false === strpos( $content, '<iframe' ) ) {
			return $content;
		}

		return (string) preg_replace_callback(
			'/<iframe\b([^>]*)>/i',
			static function ( array $matches ): string {
				$attrs = $matches[1];

				// Skip if already has loading attribute.
				if ( preg_match( '/\bloading\s*=/i', $attrs ) ) {
					return $matches[0];
				}

				return '<iframe' . $attrs . ' loading="lazy">';
			},
			$content
		);
	}

	/**
	 * Add decoding="async" to attachment image attributes.
	 *
	 * @param array<string, string> $attr Image attributes.
	 * @return array<string, string> Modified attributes.
	 */
	public function add_decode_async( array $attr ): array {
		if ( ! isset( $attr['decoding'] ) ) {
			$attr['decoding'] = 'async';
		}

		return $attr;
	}
}
