<?php
/**
 * BaseCloud Boost — Miscellaneous Optimizer
 *
 * @package BaseCloud\Boost\Optimizer
 */

declare( strict_types=1 );

namespace BaseCloud\Boost\Optimizer;

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

/**
 * Handles miscellaneous WordPress performance optimizations.
 */
class Misc_Optimizer {

	/**
	 * Boot miscellaneous optimizations.
	 *
	 * @return void
	 */
	public function boot(): void {
		if ( BCBOOST_get_option( 'clean_head', true ) ) {
			$this->clean_head();
		}

		if ( BCBOOST_get_option( 'disable_emojis', true ) ) {
			$this->disable_emojis();
		}

		if ( BCBOOST_get_option( 'disable_embeds', true ) ) {
			$this->disable_embeds();
		}

		if ( BCBOOST_get_option( 'disable_xmlrpc', true ) ) {
			$this->disable_xmlrpc();
		}

		if ( BCBOOST_get_option( 'limit_post_revisions', 5 ) !== null ) {
			$this->limit_revisions();
		}

		if ( BCBOOST_get_option( 'disable_dashicons', false ) ) {
			add_action( 'wp_enqueue_scripts', [ $this, 'remove_dashicons' ], 999 );
		}
	}

	/**
	 * Remove WordPress head tags that serve no front-end purpose:
	 * generator meta, shortlink, RSD link, Windows Live Writer manifest,
	 * and adjacent-posts rel links.
	 *
	 * @return void
	 */
	private function clean_head(): void {
		remove_action( 'wp_head', 'wp_generator' );
		remove_action( 'wp_head', 'wp_shortlink_wp_head', 10 );
		remove_action( 'wp_head', 'rsd_link' );
		remove_action( 'wp_head', 'wlwmanifest_link' );
		remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10 );

		add_filter( 'wp_headers', static function ( array $headers ): array {
			unset( $headers['X-Pingback'] );
			return $headers;
		} );
	}

	/**
	 * Disable XML-RPC entirely — reduces attack surface and eliminates
	 * the pingback DDoS amplification vector.
	 *
	 * @return void
	 */
	private function disable_xmlrpc(): void {
		add_filter( 'xmlrpc_enabled', '__return_false' );

		// Remove the X-Pingback header.
		add_filter( 'wp_headers', static function ( array $headers ): array {
			unset( $headers['X-Pingback'] );
			return $headers;
		} );

		// Remove the xmlrpc discovery link from <head>.
		remove_action( 'wp_head', 'rsd_link' );
		remove_action( 'wp_head', 'wlwmanifest_link' );
	}

	/**
	 * Remove WordPress emoji scripts, styles, and DNS prefetch.
	 *
	 * @return void
	 */
	private function disable_emojis(): void {
		remove_action( 'wp_head',             'print_emoji_detection_script', 7 );
		remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
		remove_action( 'wp_print_styles',     'print_emoji_styles' );
		remove_action( 'admin_print_styles',  'print_emoji_styles' );
		remove_filter( 'the_content_feed',    'wp_staticize_emoji' );
		remove_filter( 'comment_text_rss',    'wp_staticize_emoji' );
		remove_filter( 'wp_mail',             'wp_staticize_emoji_for_email' );

		add_filter( 'tiny_mce_plugins',  [ $this, 'disable_emojis_tinymce' ] );
		add_filter( 'wp_resource_hints', [ $this, 'disable_emojis_dns_prefetch' ], 10, 2 );
	}

	/**
	 * Remove emoji TinyMCE plugin.
	 *
	 * @param array $plugins TinyMCE plugins.
	 * @return array
	 */
	public function disable_emojis_tinymce( array $plugins ): array {
		return array_diff( $plugins, [ 'wpemoji' ] );
	}

	/**
	 * Remove emoji DNS prefetch.
	 *
	 * @param array  $hints         Resource hints.
	 * @param string $relation_type Relation type.
	 * @return array
	 */
	public function disable_emojis_dns_prefetch( array $hints, string $relation_type ): array {
		if ( $relation_type !== 'dns-prefetch' ) {
			return $hints;
		}
		return array_filter( $hints, static function ( $hint ) {
			$href = is_array( $hint ) ? ( $hint['href'] ?? '' ) : (string) $hint;
			return strpos( $href, 's.w.org' ) === false;
		} );
	}

	/**
	 * Trim WordPress's *outbound* embed plumbing WITHOUT touching embed
	 * RENDERING. This option's job is to stop serving wp-embed.min.js and the
	 * "embed this post" discovery surface of THIS site — nothing more.
	 *
	 * History (fleet incident, 2026-07-28 — "videos/iframes error and don't
	 * show"): this method used to also remove wp_filter_pre_oembed_result,
	 * wp_filter_oembed_result and the oEmbed REST routes. Those are the
	 * machinery that turns a pasted YouTube/Vimeo URL INTO an iframe:
	 * removing the REST route 404s the editor's embed-preview proxy
	 * ("Sorry, this content could not be embedded"), and removing the
	 * render filters can resolve an embed to empty HTML that the page cache
	 * then freezes. Embed rendering must never be degraded by this toggle.
	 *
	 * @return void
	 */
	private function disable_embeds(): void {
		remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );
		remove_action( 'wp_head', 'wp_oembed_add_host_js' );

		add_filter( 'rewrite_rules_array', [ $this, 'disable_embeds_rewrites' ] );
	}

	/**
	 * Remove embed rewrite rules.
	 *
	 * @param array $rules Rewrite rules.
	 * @return array
	 */
	public function disable_embeds_rewrites( array $rules ): array {
		foreach ( $rules as $rule => $rewrite ) {
			if ( strpos( $rewrite, 'embed=true' ) !== false ) {
				unset( $rules[ $rule ] );
			}
		}
		return $rules;
	}

	/**
	 * Remove Dashicons CSS for non-logged-in visitors.
	 *
	 * @return void
	 */
	public function remove_dashicons(): void {
		if ( ! is_user_logged_in() ) {
			wp_deregister_style( 'dashicons' );
		}
	}

	/**
	 * Limit the number of stored post revisions.
	 *
	 * @return void
	 */
	private function limit_revisions(): void {
		$limit = (int) BCBOOST_get_option( 'limit_post_revisions', 5 );
		add_filter( 'wp_revisions_to_keep', static function ( int $num, \WP_Post $post ) use ( $limit ): int {
			return $limit;
		}, 10, 2 );
	}
}
