<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/* -------------------------------------------------------------------------
 * Video URL parsing
 * ---------------------------------------------------------------------- */

function misticease_video_url_pattern(): string {
	return misticease_video_service_candidate_pattern();
}

function misticease_video_service_candidate_pattern(): string {
	return '/(?<!["\'=])
		   (?P<prefix>(?:\/[^\/<>"\'\n\r]+\/)?(?:(?:ttl[ \x{3000}][^\/<>"\'\n\r]+|t\d{1,4}|p\d{1,4}|r\d{1,4}|np|n)\/)*)?
		   (?P<url>https?:\/\/(?:(?:(?:www|m)\.)?youtube\.com|(?:www\.)?youtube-nocookie\.com|youtu\.be|(?:www\.)?vimeo\.com|player\.vimeo\.com|(?:www\.)?dailymotion\.com|dai\.ly)\/[^\s<>"\'　]+)
		   /ixu';
}

function misticease_normalize_video_service_url_for_detection( string $url ): string {
	$url = trim( html_entity_decode( $url, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) );
	$url = preg_replace( '#/{3}$#', '', $url );
	return is_string( $url ) ? $url : '';
}

function misticease_split_pasted_video_service_url( string $url ): array {
	$url  = trim( (string) $url );
	$tail = '';

	if ( '' === $url ) {
		return [ '', '' ];
	}

	if ( preg_match( '#/{3}([\.,。、!\?\)]*)$#u', $url, $marker_match ) ) {
		$tail = isset( $marker_match[1] ) ? (string) $marker_match[1] : '';
		$url  = substr( $url, 0, -strlen( $marker_match[0] ) );
	} elseif ( preg_match( '/([\.,。、!\?\)]+)$/u', $url, $tail_match ) ) {
		$tail = (string) $tail_match[1];
		$url  = substr( $url, 0, -strlen( $tail ) );
	}

	return [ misticease_normalize_video_service_url_for_detection( $url ), $tail ];
}

function misticease_parse_video_url_query( array $parts ): array {
	if ( empty( $parts['query'] ) ) {
		return [];
	}

	$query = html_entity_decode( (string) $parts['query'], ENT_QUOTES | ENT_HTML5, 'UTF-8' );
	parse_str( str_replace( '&amp;', '&', $query ), $params );

	return is_array( $params ) ? $params : [];
}

function misticease_first_path_segment( string $path ): string {
	$path = trim( $path, '/' );
	if ( '' === $path ) {
		return '';
	}

	$segments = explode( '/', $path );
	return isset( $segments[0] ) ? (string) $segments[0] : '';
}

function misticease_validate_youtube_id( $video_id ): string {
	if ( ! is_scalar( $video_id ) ) {
		return '';
	}

	$video_id = (string) $video_id;
	return preg_match( '/^[A-Za-z0-9_-]{11}$/', $video_id ) ? $video_id : '';
}

function misticease_validate_vimeo_id( $video_id ): string {
	if ( ! is_scalar( $video_id ) ) {
		return '';
	}

	$video_id = (string) $video_id;
	return preg_match( '/^\d{3,15}$/', $video_id ) ? $video_id : '';
}

function misticease_validate_dailymotion_id( $video_id ): string {
	if ( ! is_scalar( $video_id ) ) {
		return '';
	}

	$video_id = (string) $video_id;
	return preg_match( '/^[A-Za-z0-9]{2,32}$/', $video_id ) ? $video_id : '';
}

function misticease_parse_youtube_video_url( string $host, string $path, array $query ): array {
	$video_id = '';

	if ( 'youtu.be' === $host ) {
		$video_id = misticease_validate_youtube_id( misticease_first_path_segment( $path ) );
	} elseif ( preg_match( '/^(?:www\.|m\.)?youtube\.com$/', $host ) ) {
		if ( '/watch' === $path ) {
			$video_id = misticease_validate_youtube_id( $query['v'] ?? '' );
		} elseif ( preg_match( '~^/(?:shorts|embed|live|v)/([^/?#]+)~', $path, $m ) ) {
			$video_id = misticease_validate_youtube_id( $m[1] );
		}
	} elseif ( preg_match( '/^(?:www\.)?youtube-nocookie\.com$/', $host ) ) {
		if ( preg_match( '~^/embed/([^/?#]+)~', $path, $m ) ) {
			$video_id = misticease_validate_youtube_id( $m[1] );
		}
	}

	return '' === $video_id ? [] : [ 'type' => 'youtube', 'id' => $video_id ];
}

function misticease_parse_vimeo_video_url( string $host, string $path ): array {
	$video_id = '';

	if ( 'player.vimeo.com' === $host ) {
		if ( preg_match( '#^/video/(\d+)(?:/)?$#', $path, $m ) ) {
			$video_id = misticease_validate_vimeo_id( $m[1] );
		}
	} elseif ( preg_match( '/^(?:www\.)?vimeo\.com$/', $host ) ) {
		if ( preg_match( '#^/(\d+)(?:/)?$#', $path, $m ) ) {
			$video_id = misticease_validate_vimeo_id( $m[1] );
		}
	}

	return '' === $video_id ? [] : [ 'type' => 'vimeo', 'id' => $video_id ];
}

function misticease_parse_dailymotion_video_url( string $host, string $path ): array {
	$video_id = '';

	if ( 'dai.ly' === $host ) {
		if ( preg_match( '#^/([A-Za-z0-9]+)(?:[_/].*)?$#', $path, $m ) ) {
			$video_id = misticease_validate_dailymotion_id( $m[1] );
		}
	} elseif ( preg_match( '/^(?:www\.)?dailymotion\.com$/', $host ) ) {
		if ( preg_match( '#^/(?:video|embed/video)/([A-Za-z0-9]+)(?:[_/].*)?$#', $path, $m ) ) {
			$video_id = misticease_validate_dailymotion_id( $m[1] );
		}
	}

	return '' === $video_id ? [] : [ 'type' => 'dailymotion', 'id' => $video_id ];
}

function misticease_parse_video_service_url( string $url ): array {
	$url = misticease_normalize_video_service_url_for_detection( $url );
	if ( '' === $url || ! preg_match( '#^https?://#i', $url ) ) {
		return [];
	}

	$parts = wp_parse_url( $url );
	if ( ! is_array( $parts ) || empty( $parts['host'] ) ) {
		return [];
	}

	$host  = strtolower( preg_replace( '/:\d+$/', '', (string) $parts['host'] ) );
	$path  = isset( $parts['path'] ) ? (string) $parts['path'] : '';
	$query = misticease_parse_video_url_query( $parts );

	if ( preg_match( '/(?:^|\.)youtube(?:-nocookie)?\.com$|^youtu\.be$/', $host ) ) {
		$parsed = misticease_parse_youtube_video_url( $host, $path, $query );
	} elseif ( preg_match( '/(?:^|\.)vimeo\.com$/', $host ) ) {
		$parsed = misticease_parse_vimeo_video_url( $host, $path );
	} elseif ( preg_match( '/(?:^|\.)dailymotion\.com$|^dai\.ly$/', $host ) ) {
		$parsed = misticease_parse_dailymotion_video_url( $host, $path );
	} else {
		$parsed = [];
	}

	if ( empty( $parsed['type'] ) || empty( $parsed['id'] ) ) {
		return [];
	}

	$parsed['url'] = $url;
	return $parsed;
}

function misticease_is_supported_video_service_url( string $url ): bool {
	return [] !== misticease_parse_video_service_url( $url );
}

function misticease_video_type_from_url_parse( array $parsed ): string {
	return isset( $parsed['type'] ) ? (string) $parsed['type'] : '';
}

/* -------------------------------------------------------------------------
 * Content filters
 * ---------------------------------------------------------------------- */

function misticease_convert_anchored_ncode( string $content ): string {
	if ( false === strpos( $content, '<a' ) ) {
		return $content;
	}

	$opts           = misticease_opts_get();
	$images_enabled = ! isset( $opts['image_popup'] ) || 'on' === $opts['image_popup'];
	$pattern        = '/<a\b[^>]*\bhref=(["\'])(?P<href>[^"\']+)\1[^>]*>(?P<text>.*?)<\/a>/is';

	return (string) preg_replace_callback(
		$pattern,
		static function ( $m ) use ( $images_enabled ) {
			$anchor_html = $m[0];

			if ( stripos( $anchor_html, 'misticease-' ) !== false ) {
				return $anchor_html;
			}

			$txt = trim( wp_strip_all_tags( $m['text'] ) );
			if ( '' === $txt ) {
				return $anchor_html;
			}

			$txt  = html_entity_decode( $txt, ENT_QUOTES, 'UTF-8' );
			$href = html_entity_decode( $m['href'], ENT_QUOTES, 'UTF-8' );

			if ( ! preg_match( '/https?:\/\/\S+/i', $txt ) ) {
				return $anchor_html;
			}

			$txt_cmp  = preg_replace( '/^n\//i', '', $txt );
			$txt_cmp  = preg_replace( '#/{3}$#', '', $txt_cmp );
			$href_cmp = preg_replace( '#/{3}$#', '', $href );

			if ( 0 === strcasecmp( $href_cmp, $txt_cmp ) ) {
				return esc_html( $txt );
			}

			// The Block Editor may store the full SML-prefixed image string inside its automatic link.
			if (
				$images_enabled
				&& preg_match( misticease_direct_image_url_pattern(), $txt, $image_match )
				&& $txt === (string) $image_match[0]
			) {
				$prefix = isset( $image_match['prefix'] ) ? (string) $image_match['prefix'] : '';
				$spec   = misticease_parse_slash_markup_prefix( $prefix );

				if (
					empty( $spec['text_link'] )
					&& ( ! empty( $spec['thumb_width'] ) || ! empty( $spec['no_popup'] ) )
				) {
					[ $image_url ] = misticease_split_pasted_direct_image_url( (string) $image_match['url'] );

					if (
						'' !== $image_url
						&& misticease_is_same_site_wp_content_image_href( $image_url )
						&& 0 === strcasecmp( $href_cmp, $image_url )
					) {
						return esc_html( $txt );
					}
				}
			}

			return $anchor_html;
		},
		$content
	);
}
add_filter( 'the_content', 'misticease_convert_anchored_ncode', 3 );

function misticease_unsupported_video_service_link_html( string $url, string $label = '' ): string {
	$url = misticease_normalize_video_service_url_for_detection( $url );
	if ( '' === $url || ! preg_match( '#^https?://#i', $url ) ) {
		return '';
	}

	$text = '' !== trim( $label ) ? trim( $label ) : $url;

	return sprintf(
		'<a href="%1$s" class="misticease-unsupported-video-link" target="_blank" rel="noopener noreferrer nofollow">%2$s</a>',
		esc_url( $url ),
		esc_html( $text )
	);
}

function misticease_linkify_unsupported_video_service_urls( string $text ): string {
	return (string) preg_replace_callback(
		misticease_video_service_candidate_pattern(),
		static function ( $m ) {
			$matched = (string) $m[0];
			$prefix  = isset( $m['prefix'] ) ? (string) $m['prefix'] : '';
			$spec    = misticease_parse_slash_markup_prefix( $prefix );
			[ $url, $tail ] = misticease_split_pasted_video_service_url( (string) $m['url'] );

			if ( '' === $url || misticease_is_supported_video_service_url( $url ) ) {
				return $matched;
			}

			$link = misticease_unsupported_video_service_link_html( $url, (string) $spec['label'] );
			if ( '' === $link ) {
				return $matched;
			}

			return $link . $tail;
		},
		$text
	);

}

function misticease_parse_slash_markup_prefix( string $prefix ): array {
	$prefix = (string) $prefix;
	$spec   = [
		'label'        => '',
		'title'        => '',
		'thumb_width'  => null,
		'thumb_radius' => null,
		'popup_width'  => null,
		'text_link'    => false,
		'no_popup'     => false,
		'has_markup'   => false,
	];

	if ( '' === $prefix ) {
		return $spec;
	}

	$remaining = $prefix;

	if ( preg_match( '/^\/([^\/<>"\'\n\r]+)\//u', $remaining, $m ) ) {
		$spec['label']      = trim( (string) $m[1] );
		$spec['text_link']  = true;
		$spec['has_markup'] = true;
		$remaining          = substr( $remaining, strlen( $m[0] ) );
	}

	while ( '' !== $remaining ) {
		if ( preg_match( '/^ttl[ \x{3000}]([^\/<>"\'\n\r]+)\//iu', $remaining, $m ) ) {
			$spec['title']      = trim( (string) $m[1] );
			$spec['has_markup'] = true;
			$remaining          = substr( $remaining, strlen( $m[0] ) );
			continue;
		}

		if ( preg_match( '/^t(\d{1,4})\//i', $remaining, $m ) ) {
			$spec['thumb_width'] = min( max( 100, absint( $m[1] ) ), 4096 );
			$spec['has_markup']  = true;
			$remaining           = substr( $remaining, strlen( $m[0] ) );
			continue;
		}

		if ( preg_match( '/^p(\d{1,4})\//i', $remaining, $m ) ) {
			$spec['popup_width'] = min( max( 200, absint( $m[1] ) ), 8192 );
			$spec['has_markup']  = true;
			$remaining           = substr( $remaining, strlen( $m[0] ) );
			continue;
		}

		if ( preg_match( '/^r(\d{1,4})\//i', $remaining, $m ) ) {
			$radius               = absint( $m[1] );
			$spec['thumb_radius'] = $radius > 0 ? min( $radius, 9999 ) : null;
			$spec['has_markup']   = true;
			$remaining            = substr( $remaining, strlen( $m[0] ) );
			continue;
		}

		if ( preg_match( '/^np\//i', $remaining, $m ) ) {
			$spec['no_popup']   = true;
			$spec['has_markup'] = true;
			$remaining          = substr( $remaining, strlen( $m[0] ) );
			continue;
		}

		if ( preg_match( '/^n\//i', $remaining, $m ) ) {
			$spec['text_link']  = true;
			$spec['has_markup'] = true;
			$remaining          = substr( $remaining, strlen( $m[0] ) );
			continue;
		}

		break;
	}

	if ( '' !== $remaining ) {
		$spec['has_markup'] = false;
	}

	return $spec;
}

function misticease_video_text_link_html( string $url, string $video_id, string $type, int $popup_width, string $text, string $title = '' ): string {
	$attrs = '';
	if ( '' !== $title ) {
		$attrs = ' data-misticease-title="' . esc_attr( $title ) . '"';
	}

	return sprintf(
		'<a href="%1$s" class="misticease-n-link" data-video-id="%2$s" data-video-type="%3$s" data-popup-width="%4$d"%5$s>%6$s</a>',
		esc_url( $url ),
		esc_attr( $video_id ),
		esc_attr( $type ),
		(int) $popup_width,
		$attrs,
		esc_html( $text )
	);
}

function misticease_convert_video_urls_in_text( string $text, int $thumb_default, int $popup_default, string $loading_attr ): string {
	$converted = (string) preg_replace_callback(
		misticease_video_service_candidate_pattern(),
		static function ( $m ) use ( $thumb_default, $popup_default, $loading_attr ) {
			$matched = (string) $m[0];
			$prefix  = isset( $m['prefix'] ) ? (string) $m['prefix'] : '';
			$spec    = misticease_parse_slash_markup_prefix( $prefix );

			if ( '' !== $prefix && empty( $spec['has_markup'] ) ) {
				return $matched;
			}

			[ $url, $tail ] = misticease_split_pasted_video_service_url( (string) $m['url'] );
			$parsed = misticease_parse_video_service_url( $url );

			if ( empty( $parsed ) ) {
				$link = misticease_unsupported_video_service_link_html( $url, (string) $spec['label'] );
				return '' !== $link ? $link . $tail : $matched;
			}

			$video_id     = (string) $parsed['id'];
			$type         = misticease_video_type_from_url_parse( $parsed );
			$thumb_width  = is_int( $spec['thumb_width'] ) ? (int) $spec['thumb_width'] : $thumb_default;
			$thumb_radius = is_int( $spec['thumb_radius'] ) ? (int) $spec['thumb_radius'] : 0;
			$popup_width  = is_int( $spec['popup_width'] ) ? (int) $spec['popup_width'] : $popup_default;
			$title        = (string) $spec['title'];

			if ( ! empty( $spec['text_link'] ) ) {
				$link_text = (string) $spec['label'];
				if ( '' === $link_text ) {
					$link_text = misticease_get_video_title( $url );
				}
				if ( ! misticease_is_useful_video_title( $link_text, $url ) ) {
					$link_text = $url;
				}

				return misticease_video_text_link_html( $url, $video_id, $type, $popup_width, $link_text, $title ) . $tail;
			}

			return misticease_generate_thumbnail( $url, $video_id, $thumb_width, $popup_width, $loading_attr, $title, $thumb_radius ) . $tail;
		},
		$text
	);

	return $converted;
}

function misticease_convert_video_urls( string $content ): string {
	if ( strlen( $content ) > 200000 ) {
		return $content;
	}

	$opts          = misticease_opts_get();
	$thumb_default = max( 100, absint( $opts['thumbnail_max_width'] ?? 640 ) );
	$popup_default = max( 200, absint( $opts['popup_max_width'] ?? 854 ) );
	$loading_attr  = ( isset( $opts['lazy_load'] ) && 'on' === $opts['lazy_load'] ) ? ' loading="lazy"' : ' loading="eager"';

	$parts = preg_split(
		'/(<!--.*?-->|<!\[CDATA\[.*?\]\]>|<[^>"\']*(?:"[^"]*"[^>"\']*|\'[^\']*\'[^>"\']*)*>)/s',
		$content,
		-1,
		PREG_SPLIT_DELIM_CAPTURE
	);

	if ( ! is_array( $parts ) ) {
		return $content;
	}

	$skip_tags = [
		'a'        => 0,
		'pre'      => 0,
		'code'     => 0,
		'script'   => 0,
		'style'    => 0,
		'textarea' => 0,
		'kbd'      => 0,
		'samp'     => 0,
		'var'      => 0,
	];

	$out = '';

	foreach ( $parts as $part ) {
		if ( '' === $part ) {
			continue;
		}

		if ( isset( $part[0] ) && '<' === $part[0] ) {
			$out .= $part;

			if ( preg_match( '/^<\s*\/\s*([a-zA-Z][a-zA-Z0-9:-]*)\b/', $part, $m ) ) {
				$tag = strtolower( $m[1] );
				if ( array_key_exists( $tag, $skip_tags ) && $skip_tags[ $tag ] > 0 ) {
					$skip_tags[ $tag ]--;
				}
				continue;
			}

			if ( preg_match( '/^<\s*([a-zA-Z][a-zA-Z0-9:-]*)\b/', $part, $m ) ) {
				$tag             = strtolower( $m[1] );
				$is_self_closing = (bool) preg_match( '#/\s*>$#', $part );

				if ( array_key_exists( $tag, $skip_tags ) && ! $is_self_closing ) {
					$skip_tags[ $tag ]++;
				}
			}

			continue;
		}

		if ( array_sum( $skip_tags ) > 0 ) {
			$out .= $part;
			continue;
		}

		$out .= misticease_convert_video_urls_in_text( $part, $thumb_default, $popup_default, $loading_attr );
	}

	return $out;
}

add_filter( 'the_content', 'misticease_convert_video_urls', 5 );

function misticease_is_useful_video_title( string $title, string $url ): bool {
	$title = trim( wp_strip_all_tags( $title ) );
	$url   = trim( $url );

	if ( '' === $title ) {
		return false;
	}

	if ( preg_match( '#^https?://#i', $title ) ) {
		return false;
	}

	if ( '' !== $url && 0 === strcasecmp( untrailingslashit( $title ), untrailingslashit( $url ) ) ) {
		return false;
	}

	if ( preg_match( '/\b(?:captcha|challenge)\b/i', $title ) ) {
		return false;
	}

	return true;
}

function misticease_generate_thumbnail( string $video_url, string $video_id, int $thumb_width, int $popup_width, string $loading_attr, string $sml_title = '', int $thumb_radius = 0 ): string {
	$parsed = misticease_parse_video_service_url( $video_url );
	if ( empty( $parsed['type'] ) || empty( $parsed['id'] ) ) {
		return '';
	}
	$thumb_radius = min( max( 0, $thumb_radius ), 9999 );

	$video_type            = misticease_video_type_from_url_parse( $parsed );
	$video_id              = (string) $parsed['id'];
	$thumbnail_url         = misticease_get_thumb_local( $video_type, $video_id );
	$thumb_height          = max( 1, (int) round( $thumb_width * 9 / 16 ) );
	$fallback_title        = '';
	$is_vimeo_placeholder  = false;
	$alt_text              = '';

	if ( 'youtube' === $video_type ) {
		$alt_text = esc_attr__( 'YouTube video thumbnail', 'misticease' );
	} elseif ( 'vimeo' === $video_type ) {
		$alt_text             = esc_attr__( 'Vimeo video thumbnail', 'misticease' );
		$is_vimeo_placeholder = ( $thumbnail_url === misticease_vimeo_placeholder_url() );
		if ( $is_vimeo_placeholder ) {
			$maybe_title = misticease_get_video_title( $video_url );
			if ( misticease_is_useful_video_title( $maybe_title, $video_url ) ) {
				$fallback_title = $maybe_title;
			} else {
				$fallback_title = sprintf( 'Vimeo #%s', preg_replace( '/[^A-Za-z0-9_-]/', '', $video_id ) );
			}
		}
	} elseif ( 'dailymotion' === $video_type ) {
		$alt_text = esc_attr__( 'Dailymotion video thumbnail', 'misticease' );
	} else {
		return '';
	}

	$classes = 'misticease-thumbnail';
	if ( $is_vimeo_placeholder ) {
		$classes .= ' misticease-thumbnail--vimeo-fallback';
	}
	$thumb_style     = 'max-width:' . $thumb_width . 'px;';
	$thumb_img_style = '';
	if ( $thumb_radius > 0 ) {
		$thumb_style     .= 'border-radius:' . $thumb_radius . 'px;';
		$thumb_img_style  = 'border-radius:' . $thumb_radius . 'px;';
	}

	$aria_label = __( 'Open video pop-up', 'misticease' );
	if ( '' !== $fallback_title ) {
		$aria_label .= ': ' . $fallback_title;
	}

	ob_start(); ?>
	<div class="<?php echo esc_attr( $classes ); ?>"
	     role="button"
	     tabindex="0"
	     aria-label="<?php echo esc_attr( $aria_label ); ?>"
	     data-video-id="<?php echo esc_attr( $video_id ); ?>"
	     data-video-type="<?php echo esc_attr( $video_type ); ?>"
	     data-popup-width="<?php echo esc_attr( $popup_width ); ?>"
	     <?php if ( '' !== $sml_title ) : ?>
	     data-misticease-title="<?php echo esc_attr( $sml_title ); ?>"
	     <?php endif; ?>
	     style="<?php echo esc_attr( $thumb_style ); ?>">
		<img decoding="async"
		     src="<?php echo esc_url( $thumbnail_url ); ?>"
		     <?php echo $loading_attr; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Controlled loading attribute from plugin settings. ?>
		     alt="<?php echo esc_attr( $alt_text ); ?>"
		     width="<?php echo (int) $thumb_width; ?>"
		     height="<?php echo (int) $thumb_height; ?>"
		     <?php if ( '' !== $thumb_img_style ) : ?>
		     style="<?php echo esc_attr( $thumb_img_style ); ?>"
		     <?php endif; ?>>
		<?php if ( $is_vimeo_placeholder ) : ?>
			<span class="misticease-vimeo-fallback-card" aria-hidden="true">
				<span class="misticease-vimeo-fallback-card__provider">Vimeo</span>
				<span class="misticease-vimeo-fallback-card__title"><?php echo esc_html( $fallback_title ); ?></span>
				<span class="misticease-vimeo-fallback-card__action"><?php echo esc_html__( 'Open video pop-up', 'misticease' ); ?></span>
			</span>
		<?php endif; ?>
		<span class="play-button" aria-hidden="true">
			<img decoding="async" src="<?php echo esc_url( plugins_url( 'inc/app/img/play.png', misticease_basefile() ) ); ?>" width="60" height="60" alt="" aria-hidden="true" />
		</span>
	</div>
	<?php
	return (string) ob_get_clean();
}

/* -------------------------------------------------------------------------
 * Core embed override
 * ---------------------------------------------------------------------- */

function misticease_embed_block_url( array $parsed_block ): string {
	if ( 'core/embed' !== ( $parsed_block['blockName'] ?? '' ) ) {
		return '';
	}

	$attrs = isset( $parsed_block['attrs'] ) && is_array( $parsed_block['attrs'] ) ? $parsed_block['attrs'] : [];
	if ( isset( $attrs['url'] ) && is_scalar( $attrs['url'] ) ) {
		$parsed = misticease_parse_video_service_url( (string) $attrs['url'] );
		if ( ! empty( $parsed['url'] ) ) {
			return (string) $parsed['url'];
		}
	}

	if ( isset( $parsed_block['innerHTML'] ) && is_string( $parsed_block['innerHTML'] ) ) {
		if ( preg_match_all( '#https?://[^\s<>"\']+#i', $parsed_block['innerHTML'], $matches ) ) {
			foreach ( $matches[0] as $candidate ) {
				$parsed = misticease_parse_video_service_url( html_entity_decode( (string) $candidate, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) );
				if ( ! empty( $parsed['url'] ) ) {
					return (string) $parsed['url'];
				}
			}
		}
	}

	return '';
}

function misticease_embed_block_classes( array $parsed_block ): string {
	$classes = [ 'wp-block-embed', 'misticease-embed-block' ];
	$attrs   = isset( $parsed_block['attrs'] ) && is_array( $parsed_block['attrs'] ) ? $parsed_block['attrs'] : [];

	if ( isset( $attrs['className'] ) && is_scalar( $attrs['className'] ) ) {
		$classes = array_merge( $classes, preg_split( '/\s+/', trim( (string) $attrs['className'] ) ) ?: [] );
	}

	if ( isset( $attrs['type'] ) && is_scalar( $attrs['type'] ) ) {
		$type = sanitize_key( (string) $attrs['type'] );
		if ( '' !== $type ) {
			$classes[] = 'is-type-' . $type;
		}
	}

	if ( isset( $attrs['providerNameSlug'] ) && is_scalar( $attrs['providerNameSlug'] ) ) {
		$provider = sanitize_key( (string) $attrs['providerNameSlug'] );
		if ( '' !== $provider ) {
			$classes[] = 'is-provider-' . $provider;
			$classes[] = 'wp-block-embed-' . $provider;
		}
	}

	if ( isset( $attrs['align'] ) && is_scalar( $attrs['align'] ) ) {
		$align = sanitize_key( (string) $attrs['align'] );
		if ( in_array( $align, [ 'left', 'center', 'right', 'wide', 'full' ], true ) ) {
			$classes[] = 'align' . $align;
		}
	}

	if ( isset( $parsed_block['innerHTML'] ) && is_string( $parsed_block['innerHTML'] ) ) {
		if ( preg_match( '/<figure\b[^>]*\bclass=([\'"])(.*?)\1/is', $parsed_block['innerHTML'], $match ) ) {
			$classes = array_merge( $classes, preg_split( '/\s+/', trim( html_entity_decode( (string) $match[2], ENT_QUOTES | ENT_HTML5, 'UTF-8' ) ) ) ?: [] );
		}
	}

	$classes = array_filter(
		array_map(
			static function ( $class ): string {
				return sanitize_html_class( (string) $class );
			},
			$classes
		)
	);

	return implode( ' ', array_values( array_unique( $classes ) ) );
}

function misticease_embed_block_caption( array $parsed_block ): string {
	$attrs   = isset( $parsed_block['attrs'] ) && is_array( $parsed_block['attrs'] ) ? $parsed_block['attrs'] : [];
	$caption = isset( $attrs['caption'] ) && is_scalar( $attrs['caption'] ) ? trim( (string) $attrs['caption'] ) : '';

	if ( '' === $caption && isset( $parsed_block['innerHTML'] ) && is_string( $parsed_block['innerHTML'] ) ) {
		if ( preg_match( '#<figcaption\b[^>]*>(.*?)</figcaption>#is', $parsed_block['innerHTML'], $match ) ) {
			$caption = trim( (string) $match[1] );
		}
	}

	return '' !== $caption ? wp_kses_post( $caption ) : '';
}

function misticease_pre_render_embed_block( $pre_render, array $parsed_block ) {
	$url = misticease_embed_block_url( $parsed_block );
	if ( '' === $url ) {
		return $pre_render;
	}

	$parsed = misticease_parse_video_service_url( $url );
	if ( empty( $parsed['id'] ) ) {
		return $pre_render;
	}

	$opts         = misticease_opts_get();
	$thumb_width  = max( 100, absint( $opts['thumbnail_max_width'] ?? 640 ) );
	$popup_width  = max( 200, absint( $opts['popup_max_width'] ?? 854 ) );
	$loading_attr = ( isset( $opts['lazy_load'] ) && 'on' === $opts['lazy_load'] ) ? ' loading="lazy"' : ' loading="eager"';
	$thumbnail    = misticease_generate_thumbnail( $url, (string) $parsed['id'], $thumb_width, $popup_width, $loading_attr );

	if ( '' === $thumbnail ) {
		return $pre_render;
	}

	$caption = misticease_embed_block_caption( $parsed_block );
	if ( '' !== $caption ) {
		$caption = '<figcaption class="wp-element-caption">' . $caption . '</figcaption>';
	}

	return '<figure class="' . esc_attr( misticease_embed_block_classes( $parsed_block ) ) . '">' . $thumbnail . $caption . '</figure>';
}
add_filter( 'pre_render_block', 'misticease_pre_render_embed_block', PHP_INT_MAX, 2 );

/* -------------------------------------------------------------------------
 * Rendered block conversion
 * ---------------------------------------------------------------------- */

function misticease_convert_rendered_video_block( string $block_content, array $parsed_block ): string {
	if ( '' === $block_content || 'core/embed' === ( $parsed_block['blockName'] ?? '' ) ) {
		return $block_content;
	}

	if ( ! preg_match( '#(?:youtu\.be/|youtube(?:-nocookie)?\.com/|vimeo\.com/|dailymotion\.com/|dai\.ly/)#i', $block_content ) ) {
		return $block_content;
	}

	$block_content = misticease_convert_anchored_ncode( $block_content );
	return misticease_convert_video_urls( $block_content );
}
add_filter( 'render_block', 'misticease_convert_rendered_video_block', 5, 2 );

/* -------------------------------------------------------------------------
 * Mark WP media anchors (respect CDN effective list)
 * ---------------------------------------------------------------------- */

function misticease_normalize_image_popup_host( string $host ): string {
	$host = trim( strtolower( $host ) );
	if ( '' === $host ) {
		return '';
	}
	$source = $host;
	if ( 0 === strpos( $source, '//' ) ) {
		$source = 'https:' . $source;
	} elseif ( false === strpos( $source, '://' ) ) {
		$source = 'https://' . $source;
	}
	$parsed = wp_parse_url( $source );
	if ( is_array( $parsed ) && ! empty( $parsed['host'] ) ) {
		return trim( strtolower( (string) $parsed['host'] ) );
	}
	return (string) preg_replace( '/:\d+$/', '', $host );
}

function misticease_is_supported_media_image_href( string $href, array $cdn_hosts ): bool {
	$href = trim( html_entity_decode( $href, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) );
	if ( '' === $href || preg_match( '#^(?:javascript|data|mailto|tel):#i', $href ) ) {
		return false;
	}

	$parsed_href = $href;
	if ( 0 === strpos( $parsed_href, '//' ) ) {
		$parsed_href = ( is_ssl() ? 'https:' : 'http:' ) . $parsed_href;
	}

	$parts = wp_parse_url( $parsed_href );
	if ( ! is_array( $parts ) ) {
		return false;
	}

	$path = isset( $parts['path'] ) ? (string) $parts['path'] : '';
	if ( '' === $path ) {
		return false;
	}

	$ext = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) );
	if ( ! in_array( $ext, array( 'jpg', 'jpeg', 'png', 'gif', 'webp', 'avif' ), true ) ) {
		return false;
	}

	$host      = isset( $parts['host'] ) ? misticease_normalize_image_popup_host( (string) $parts['host'] ) : '';
	$cdn_hosts = array_filter( array_map( 'misticease_normalize_image_popup_host', $cdn_hosts ) );
	if ( '' !== $host && in_array( $host, $cdn_hosts, true ) ) {
		return true;
	}

	if ( ! preg_match( '#(?:^|/)wp-content/#i', $path ) ) {
		return false;
	}

	if ( '' === $host ) {
		return true;
	}

	$allowed_hosts = array_filter(
		array_unique(
			array_map(
				'misticease_normalize_image_popup_host',
				array(
					home_url(),
					site_url(),
					content_url(),
				)
			)
		)
	);

	return in_array( $host, $allowed_hosts, true );
}

function misticease_anchor_opening_has_class( string $open, string $class_name ): bool {
	return (bool) preg_match( '/\bclass\s*=\s*([\'\"])[^\'\"]*\b' . preg_quote( $class_name, '/' ) . '\b/i', $open );
}

function misticease_anchor_opening_add_class( string $open, string $class_name ): string {
	if ( misticease_anchor_opening_has_class( $open, $class_name ) ) {
		return $open;
	}
	if ( preg_match( '/\bclass\s*=\s*([\'\"])(.*?)\1/i', $open ) ) {
		return (string) preg_replace(
			'/\bclass\s*=\s*([\'\"])(.*?)\1/i',
			'class=$1$2 ' . $class_name . '$1',
			$open,
			1
		);
	}
	return (string) preg_replace( '/^<a\b/i', '<a class="' . $class_name . '"', $open, 1 );
}


function misticease_direct_image_url_pattern(): string {
	return '/(?<!["\'=])
		   (?P<prefix>(?:\/[^\/<>":\'\n\r]+\/)?(?:(?:ttl[ \x{3000}][^\/<>":\'\n\r]+|t\d{1,4}|p\d{1,4}|r\d{1,4}|np|n)\/)*)?
		   (?P<url>https?:\/\/[^\s<>"\'　]+?\.(?:jpe?g|png|gif|webp|avif)(?:[?#][^\s<>"\'　]*)?(?:\/{3}[\.,。、!\?\)]*)?)
		   /ixu';
}

function misticease_is_same_site_wp_content_image_href( string $href ): bool {
	$href = trim( html_entity_decode( $href, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) );
	if ( '' === $href || preg_match( '#^(?:javascript|data|mailto|tel):#i', $href ) ) {
		return false;
	}

	$parts = wp_parse_url( $href );
	if ( ! is_array( $parts ) || empty( $parts['host'] ) ) {
		return false;
	}

	$path = isset( $parts['path'] ) ? (string) $parts['path'] : '';
	if ( '' === $path || ! preg_match( '#(?:^|/)wp-content/#i', $path ) ) {
		return false;
	}

	$ext = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) );
	if ( ! in_array( $ext, array( 'jpg', 'jpeg', 'png', 'gif', 'webp', 'avif' ), true ) ) {
		return false;
	}

	$host          = misticease_normalize_image_popup_host( (string) $parts['host'] );
	$allowed_hosts = array_filter(
		array_unique(
			array_map(
				'misticease_normalize_image_popup_host',
				array(
					home_url(),
					site_url(),
					content_url(),
				)
			)
		)
	);

	return '' !== $host && in_array( $host, $allowed_hosts, true );
}

function misticease_split_pasted_direct_image_url( string $url ): array {
	$url  = trim( html_entity_decode( (string) $url, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) );
	$tail = '';

	if ( '' === $url ) {
		return [ '', '' ];
	}

	if ( preg_match( '#/{3}([\.,。、!\?\)]*)$#u', $url, $marker_match ) ) {
		$tail = isset( $marker_match[1] ) ? (string) $marker_match[1] : '';
		$url  = substr( $url, 0, -strlen( $marker_match[0] ) );
	} elseif ( preg_match( '/([\.,。、!\?\)]+)$/u', $url, $tail_match ) ) {
		$tail = (string) $tail_match[1];
		$url  = substr( $url, 0, -strlen( $tail ) );
	}

	return [ trim( $url ), $tail ];
}

function misticease_direct_image_thumbnail_html( string $url, array $spec, string $loading_attr ): string {
	$thumb_width = isset( $spec['thumb_width'] ) && is_int( $spec['thumb_width'] ) ? (int) $spec['thumb_width'] : 0;
	if ( $thumb_width < 1 ) {
		return '';
	}

	$popup_width  = isset( $spec['popup_width'] ) && is_int( $spec['popup_width'] ) ? (int) $spec['popup_width'] : 0;
	$thumb_radius = isset( $spec['thumb_radius'] ) && is_int( $spec['thumb_radius'] ) ? min( max( 0, (int) $spec['thumb_radius'] ), 9999 ) : 0;
	$title        = isset( $spec['title'] ) ? trim( (string) $spec['title'] ) : '';
	$label        = isset( $spec['label'] ) ? trim( (string) $spec['label'] ) : '';
	$alt_text     = '' !== $title ? $title : ( '' !== $label ? $label : __( 'Image thumbnail', 'misticease' ) );

	$no_popup = ! empty( $spec['no_popup'] );
	$thumb_style = sprintf( 'width:100%%;max-width:%dpx;height:auto;', $thumb_width );
	if ( $thumb_radius > 0 ) {
		$thumb_style .= 'border-radius:' . $thumb_radius . 'px;';
	}

	if ( $no_popup ) {
		return sprintf(
			'<img decoding="async" src="%1$s"%2$s alt="%3$s" width="%4$d" style="%5$s" class="misticease-np" data-misticease-np="1">',
			esc_url( $url ),
			$loading_attr,
			esc_attr( $alt_text ),
			$thumb_width,
			esc_attr( $thumb_style )
		);
	}

	$attrs      = ' role="button" tabindex="0"';
	$aria_label = sprintf(
		/* translators: %s: image label or generic image thumbnail text. */
		__( 'Open image pop-up: %s', 'misticease' ),
		$alt_text
	);
	$attrs .= ' aria-label="' . esc_attr( $aria_label ) . '"';
	if ( $popup_width > 0 ) {
		$attrs .= ' data-popup-width="' . esc_attr( (string) $popup_width ) . '"';
	}
	if ( '' !== $title ) {
		$attrs .= ' data-misticease-title="' . esc_attr( $title ) . '"';
	}
	return sprintf(
		'<a href="%1$s" class="misticease-img-popup"%2$s><img decoding="async" src="%1$s"%3$s alt="%4$s" width="%5$d" style="%6$s"></a>',
		esc_url( $url ),
		$attrs,
		$loading_attr,
		esc_attr( $alt_text ),
		$thumb_width,
		esc_attr( $thumb_style )
	);
}

function misticease_convert_direct_image_urls_in_text( string $text, int $thumb_default, string $loading_attr ): string {
	return (string) preg_replace_callback(
		misticease_direct_image_url_pattern(),
		static function ( $m ) use ( $thumb_default, $loading_attr ) {
			$matched = (string) $m[0];
			$prefix  = isset( $m['prefix'] ) ? (string) $m['prefix'] : '';
			$spec    = misticease_parse_slash_markup_prefix( $prefix );

			if ( '' === $prefix || empty( $spec['has_markup'] ) ) {
				return $matched;
			}

			// tN/ creates a sized image thumbnail. np/ alone creates a plain inline image
			// using the configured default thumbnail width. Text-link modes never create an image.
			if ( ! empty( $spec['text_link'] ) ) {
				return $matched;
			}

			if ( empty( $spec['thumb_width'] ) ) {
				if ( empty( $spec['no_popup'] ) ) {
					return $matched;
				}

				$spec['thumb_width'] = $thumb_default;
			}

			[ $url, $tail ] = misticease_split_pasted_direct_image_url( (string) $m['url'] );
			if ( '' === $url || ! misticease_is_same_site_wp_content_image_href( $url ) ) {
				return $matched;
			}

			$html = misticease_direct_image_thumbnail_html( $url, $spec, $loading_attr );
			return '' !== $html ? $html . $tail : $matched;
		},
		$text
	);
}

function misticease_convert_direct_image_urls( string $content ): string {
	if ( strlen( $content ) > 200000 ) {
		return $content;
	}

	$o = misticease_opts_get();
	if ( isset( $o['image_popup'] ) && 'on' !== $o['image_popup'] ) {
		return $content;
	}

	$thumb_default = max( 100, absint( $o['thumbnail_max_width'] ?? 640 ) );
	$loading_attr  = ( isset( $o['lazy_load'] ) && 'on' === $o['lazy_load'] ) ? ' loading="lazy"' : ' loading="eager"';

	$parts = preg_split(
		'/(<!--.*?-->|<!\[CDATA\[.*?\]\]>|<[^>"\']*(?:"[^"]*"[^>"\']*|\'[^\']*\'[^>"\']*)*>)/s',
		$content,
		-1,
		PREG_SPLIT_DELIM_CAPTURE
	);

	if ( ! is_array( $parts ) ) {
		return $content;
	}

	$skip_tags = [
		'a'        => 0,
		'pre'      => 0,
		'code'     => 0,
		'script'   => 0,
		'style'    => 0,
		'textarea' => 0,
		'kbd'      => 0,
		'samp'     => 0,
		'var'      => 0,
	];

	$out = '';

	foreach ( $parts as $part ) {
		if ( '' === $part ) {
			continue;
		}

		if ( isset( $part[0] ) && '<' === $part[0] ) {
			$out .= $part;

			if ( preg_match( '/^<\s*\/\s*([a-zA-Z][a-zA-Z0-9:-]*)\b/', $part, $m ) ) {
				$tag = strtolower( $m[1] );
				if ( array_key_exists( $tag, $skip_tags ) && $skip_tags[ $tag ] > 0 ) {
					$skip_tags[ $tag ]--;
				}
				continue;
			}

			if ( preg_match( '/^<\s*([a-zA-Z][a-zA-Z0-9:-]*)\b/', $part, $m ) ) {
				$tag             = strtolower( $m[1] );
				$is_self_closing = (bool) preg_match( '#/\s*>$#', $part );

				if ( array_key_exists( $tag, $skip_tags ) && ! $is_self_closing ) {
					$skip_tags[ $tag ]++;
				}
			}

			continue;
		}

		if ( array_sum( $skip_tags ) > 0 ) {
			$out .= $part;
			continue;
		}

		$out .= misticease_convert_direct_image_urls_in_text( $part, $thumb_default, $loading_attr );
	}

	return $out;
}

add_filter( 'the_content', 'misticease_convert_direct_image_urls', 5 );

function misticease_mark_wp_media_anchors( string $content ): string {
	$o = misticease_opts_get();
	if ( isset( $o['image_popup'] ) && 'on' !== $o['image_popup'] ) {
		return $content;
	}
	if ( stripos( $content, '<a' ) === false ) {
		return $content;
	}

	$cdn_hosts = misticease_cdn_hosts_effective();

	return (string) preg_replace_callback(
		'#<a\b[^>]*>#i',
		static function ( $m ) use ( $cdn_hosts ) {
			$open = $m[0];
			if ( misticease_anchor_opening_has_class( $open, 'misticease-img-popup' ) ) {
				return $open;
			}
			if ( ! preg_match( '/\bhref\s*=\s*([\'\"])(.*?)\1/i', $open, $href_match ) ) {
				return $open;
			}
			if ( ! misticease_is_supported_media_image_href( (string) $href_match[2], $cdn_hosts ) ) {
				return $open;
			}
			return misticease_anchor_opening_add_class( $open, 'misticease-img-popup' );
		},
		$content
	);
}

add_filter( 'the_content', 'misticease_mark_wp_media_anchors', 6 );