<?php
/**
 * BaseCloud Boost — Video Facade (Lite Embed)
 *
 * Replaces YouTube and Vimeo <iframe> embeds with lightweight thumbnail
 * facades. The player is loaded only when the user clicks the play button,
 * eliminating all third-party network requests on page load.
 *
 * Savings per YouTube embed: ~500 KB JS + 3–10 network round-trips.
 *
 * Supports:
 *  - Standard YouTube iframe embeds (youtube.com/embed, youtu.be)
 *  - YouTube nocookie embeds (youtube-nocookie.com)
 *  - Vimeo embeds (player.vimeo.com)
 *
 * @package BaseCloud\Boost\Optimizer
 */

declare( strict_types=1 );

namespace BaseCloud\Boost\Optimizer;

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

/**
 * Replaces YouTube/Vimeo iframes with click-to-play facades.
 */
class Video_Facade {

	/** CSS class prefix for facade elements. */
	private const CSS_CLASS = 'bcboost-video-facade';

	/**
	 * Boot video facade.
	 *
	 * @return void
	 */
	public function boot(): void {
		// OPT-IN since 1.3.8. Default-on facades broke real sites: a restrictive
		// Permissions-Policy (autoplay=(), encrypted-media=()) makes the clicked
		// player error out, builder video widgets fight the replacement, and the
		// click-to-play UX surprises owners ("my video stopped playing").
		if ( ! BCBOOST_get_option( 'video_facade', false ) ) {
			return;
		}

		add_filter( 'BCBOOST_buffer',         [ $this, 'process_html' ] );
		add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_styles' ], 5 );
		add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ], 5 );
	}

	/**
	 * Replace YouTube/Vimeo iframes with facades in the HTML output.
	 *
	 * @param string $html Full page HTML.
	 * @return string Modified HTML.
	 */
	public function process_html( string $html ): string {
		// Replace YouTube iframes.
		$html = preg_replace_callback(
			'/<iframe([^>]*)\bsrc=["\']([^"\']*(?:youtube(?:-nocookie)?\.com\/embed|youtu\.be\/)[^"\']*)["\']([^>]*)><\/iframe>/i',
			[ $this, 'replace_youtube' ],
			$html
		) ?? $html;

		// Replace Vimeo iframes.
		$html = preg_replace_callback(
			'/<iframe([^>]*)\bsrc=["\']([^"\']*player\.vimeo\.com\/video\/[^"\']*)["\']([^>]*)><\/iframe>/i',
			[ $this, 'replace_vimeo' ],
			$html
		) ?? $html;

		return $html;
	}

	/**
	 * Build a YouTube facade element.
	 *
	 * @param array $m Regex matches: [0]=full tag, [1]=attrs before src, [2]=src, [3]=attrs after src.
	 * @return string Facade HTML.
	 */
	private function replace_youtube( array $m ): string {
		$src        = $m[2];
		$all_attrs  = $m[1] . $m[3];
		$video_id   = $this->extract_youtube_id( $src );

		if ( ! $video_id ) {
			return $m[0]; // Can't extract ID — leave original.
		}

		$title  = $this->extract_attr( $all_attrs, 'title' ) ?: 'YouTube video';
		$width  = $this->extract_attr( $all_attrs, 'width' ) ?: '560';
		$height = $this->extract_attr( $all_attrs, 'height' ) ?: '315';

		// Autoplay param for when the facade is clicked.
		$autoplay_src = add_query_arg( 'autoplay', '1', $src );

		$thumb_url  = esc_url( "https://i.ytimg.com/vi/{$video_id}/hqdefault.jpg" );
		$thumb_webp = esc_url( "https://i.ytimg.com/vi_webp/{$video_id}/hqdefault.webp" );

		$ratio = $width && $height ? round( ( (int) $height / (int) $width ) * 100, 4 ) : 56.25;

		return sprintf(
			'<div class="%1$s %1$s--youtube" style="--bcboost-vf-ratio:%2$s%%" data-src="%3$s" role="button" tabindex="0" aria-label="%4$s">' .
			'<picture><source srcset="%5$s" type="image/webp"><img src="%6$s" alt="%4$s" loading="lazy" decoding="async"></picture>' .
			'<button class="%1$s__play" aria-label="%7$s">' .
			'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 48" aria-hidden="true"><path d="M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55c-2.93.78-4.63 3.26-5.42 6.19C.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z" fill="#f00"/><path d="M45 24L27 14v20" fill="#fff"/></svg>' .
			'</button>' .
			'</div>',
			esc_attr( self::CSS_CLASS ),
			esc_attr( (string) $ratio ),
			esc_attr( $autoplay_src ),
			esc_attr( $title ),
			$thumb_webp,
			$thumb_url,
			esc_attr__( 'Play video', 'basecloud-boost' )
		);
	}

	/**
	 * Build a Vimeo facade element.
	 *
	 * @param array $m Regex matches.
	 * @return string Facade HTML.
	 */
	private function replace_vimeo( array $m ): string {
		$src       = $m[2];
		$all_attrs = $m[1] . $m[3];
		$video_id  = $this->extract_vimeo_id( $src );

		if ( ! $video_id ) {
			return $m[0];
		}

		$title  = $this->extract_attr( $all_attrs, 'title' ) ?: 'Vimeo video';
		$width  = $this->extract_attr( $all_attrs, 'width' ) ?: '640';
		$height = $this->extract_attr( $all_attrs, 'height' ) ?: '360';
		$ratio  = $width && $height ? round( ( (int) $height / (int) $width ) * 100, 4 ) : 56.25;

		$autoplay_src = add_query_arg( 'autoplay', '1', $src );
		// Vimeo thumbnail requires an API call at runtime; use a data attribute and JS fetch.
		return sprintf(
			'<div class="%1$s %1$s--vimeo" style="--bcboost-vf-ratio:%2$s%%" data-src="%3$s" data-vimeo-id="%4$s" role="button" tabindex="0" aria-label="%5$s">' .
			'<div class="%1$s__thumb-placeholder"></div>' .
			'<button class="%1$s__play" aria-label="%6$s">' .
			'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#1AB7EA" aria-hidden="true"><path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm-2 16.5v-9l7 4.5-7 4.5z"/></svg>' .
			'</button>' .
			'</div>',
			esc_attr( self::CSS_CLASS ),
			esc_attr( (string) $ratio ),
			esc_attr( $autoplay_src ),
			esc_attr( $video_id ),
			esc_attr( $title ),
			esc_attr__( 'Play video', 'basecloud-boost' )
		);
	}

	/**
	 * Enqueue facade CSS via wp_add_inline_style.
	 *
	 * @return void
	 */
	public function enqueue_styles(): void {
		if ( is_admin() || is_feed() ) {
			return;
		}
		$c   = self::CSS_CLASS;
		$css = ".{$c}{position:relative;display:block;cursor:pointer;overflow:hidden;background:#000;max-width:100%}"
			. ".{$c}::before{display:block;content:'';padding-top:var(--bcboost-vf-ratio,56.25%)}"
			. ".{$c} img,.{$c}__thumb-placeholder{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;border:0;transition:opacity .3s}"
			. ".{$c}__thumb-placeholder{background:#222}"
			. ".{$c}:hover img{opacity:.85}"
			. ".{$c} iframe{position:absolute;top:0;left:0;width:100%;height:100%;border:0}"
			. ".{$c}__play{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:68px;height:48px;padding:0;border:0;background:transparent;cursor:pointer;transition:transform .2s}"
			. ".{$c}:hover .{$c}__play{transform:translate(-50%,-50%) scale(1.1)}"
			. ".{$c}__play svg{width:100%;height:100%}"
			. ".{$c}--vimeo .{$c}__play svg{width:48px;height:48px}";

		wp_register_style( 'bcboost-video-facade', false, [], BCBOOST_VERSION ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters
		wp_enqueue_style( 'bcboost-video-facade' );
		wp_add_inline_style( 'bcboost-video-facade', $css );
	}

	/**
	 * Enqueue facade JS loader via wp_add_inline_script.
	 *
	 * @return void
	 */
	public function enqueue_scripts(): void {
		if ( is_admin() || is_feed() ) {
			return;
		}
		$c  = self::CSS_CLASS;
		$js = "(function(){"
			. "var C='." . esc_js( $c ) . "';"
			. "function load(el){"
			. "var src=el.getAttribute('data-src');if(!src)return;"
			. "var f=document.createElement('iframe');"
			. "f.src=src;"
			. "f.allow='accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture';"
			. "f.allowFullscreen=true;f.loading='eager';"
			. "el.innerHTML='';el.appendChild(f);el.style.cursor='default';}"
			. "function fetchVimeoThumb(el){"
			. "var id=el.getAttribute('data-vimeo-id');if(!id)return;"
			. "var img=document.createElement('img');"
			. "img.loading='lazy';img.decoding='async';"
			. "img.alt=el.getAttribute('aria-label')||'Vimeo video';"
			. "var ph=el.querySelector('." . esc_js( $c ) . "__thumb-placeholder');"
			. "fetch('https://vimeo.com/api/v2/video/'+id+'.json')"
			. ".then(function(r){return r.json();})"
			. ".then(function(d){if(d&&d[0]&&d[0].thumbnail_large){img.src=d[0].thumbnail_large;if(ph)ph.parentNode.replaceChild(img,ph);}})"
			. ".catch(function(){});}"
			. "document.querySelectorAll(C).forEach(function(el){"
			. "if(el.classList.contains('" . esc_js( $c ) . "--vimeo'))fetchVimeoThumb(el);"
			. "el.addEventListener('click',function(){load(el);});"
			. "el.addEventListener('keydown',function(e){if(e.key==='Enter'||e.key===' '){e.preventDefault();load(el);}});"
			. "});"
			. "})();";

		wp_register_script( 'bcboost-video-facade', false, [], BCBOOST_VERSION, [ 'in_footer' => true ] ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters
		wp_enqueue_script( 'bcboost-video-facade' );
		wp_add_inline_script( 'bcboost-video-facade', $js );
	}

	// ─── Helpers ──────────────────────────────────────────────────────────────

	/**
	 * Extract YouTube video ID from an embed or watch URL.
	 *
	 * @param string $url Source URL.
	 * @return string|null Video ID or null.
	 */
	private function extract_youtube_id( string $url ): ?string {
		// youtube.com/embed/VIDEO_ID or youtube-nocookie.com/embed/VIDEO_ID
		if ( preg_match( '#/embed/([a-zA-Z0-9_-]{11})#', $url, $m ) ) {
			return $m[1];
		}
		// youtu.be/VIDEO_ID
		if ( preg_match( '#youtu\.be/([a-zA-Z0-9_-]{11})#', $url, $m ) ) {
			return $m[1];
		}
		return null;
	}

	/**
	 * Extract Vimeo video ID from a player embed URL.
	 *
	 * @param string $url Source URL.
	 * @return string|null Video ID or null.
	 */
	private function extract_vimeo_id( string $url ): ?string {
		if ( preg_match( '#player\.vimeo\.com/video/(\d+)#', $url, $m ) ) {
			return $m[1];
		}
		return null;
	}

	/**
	 * Extract a specific attribute value from an HTML attributes string.
	 *
	 * @param string $attrs     HTML attribute string.
	 * @param string $attr_name Attribute name.
	 * @return string Attribute value or empty string.
	 */
	private function extract_attr( string $attrs, string $attr_name ): string {
		if ( preg_match( '/\b' . preg_quote( $attr_name, '/' ) . '\s*=\s*["\']([^"\']*)["\']/', $attrs, $m ) ) {
			return $m[1];
		}
		return '';
	}
}
