<?php
/**
 * BaseCloud Boost — BunnyCDN URL Rewriter
 *
 * Rewrites wp-content and wp-includes asset URLs in the HTML output
 * so that static assets (images, CSS, JS) are served from the BunnyCDN
 * Pull Zone hostname instead of the origin server.
 *
 * Requires the "CDN Hostname" option to be set in the CDN settings page.
 * The purge integration (BunnyCDN_Integration) handles cache invalidation;
 * this class handles delivery.
 *
 * @package BaseCloud\Boost\CDN
 */

declare( strict_types=1 );

namespace BaseCloud\Boost\CDN;

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

/**
 * Rewrites static asset URLs to the BunnyCDN Pull Zone hostname.
 */
class BunnyCDN_Rewriter {

	/** @var string Origin site URL with no trailing slash. */
	private string $site_url;

	/** @var string BunnyCDN CDN URL with no trailing slash. */
	private string $cdn_url;

	/** @var int Output buffer level before our ob_start call. */
	private int $ob_level = 0;

	/**
	 * Register the output buffer hook.
	 *
	 * @return void
	 */
	public function boot(): void {
		$hostname = trim( (string) BCBOOST_get_option( 'bunnycdn_hostname', '' ) );

		if ( empty( $hostname ) ) {
			return;
		}

		// Normalize: strip any protocol the user may have entered.
		$hostname = preg_replace( '#^https?://#', '', $hostname );
		$hostname = rtrim( $hostname, '/' );

		$this->site_url = rtrim( get_option( 'siteurl' ), '/' );
		$this->cdn_url  = 'https://' . $hostname;

		// Don't rewrite when site URL and CDN URL are the same (custom hostname pointing to same domain).
		if ( $this->site_url === $this->cdn_url ) {
			return;
		}

		// Auto-preconnect the CDN origin so the browser warms TCP/TLS before the
		// first CDN asset (very often the LCP hero) is fetched. Pure resource hint
		// that rides the shared Resource_Hints preconnect output; the same-origin
		// guard above already proves this host is cross-origin. No `crossorigin`:
		// images/CSS/JS from the pull zone load as plain (non-CORS) requests, and a
		// CORS-mode preconnect would not be reused for them.
		if ( BCBOOST_get_option( 'auto_preconnect', true ) ) {
			$cdn_origin = $this->cdn_url;
			add_filter( 'BCBOOST_preconnect_origins', static function ( $origins ) use ( $cdn_origin ) {
				if ( is_array( $origins ) && ! isset( $origins[ $cdn_origin ] ) ) {
					$origins[ $cdn_origin ] = false;
				}
				return $origins;
			} );
		}

		add_action( 'template_redirect', [ $this, 'start_buffer' ], 0 );
	}

	/**
	 * Start the output buffer.
	 *
	 * @return void
	 */
	public function start_buffer(): void {
		$this->ob_level = ob_get_level();
		ob_start( [ $this, 'rewrite' ] );
		add_action( 'shutdown', [ $this, 'end_output_buffer' ], PHP_INT_MAX );
	}

	/**
	 * Explicitly close our output buffer on shutdown.
	 *
	 * @return void
	 */
	public function end_output_buffer(): void {
		if ( ob_get_level() > $this->ob_level ) {
			ob_end_flush();
		}
	}

	/**
	 * Replace origin asset URLs with the CDN hostname in the HTML output.
	 *
	 * Only rewrites wp-content and wp-includes paths — not page URLs,
	 * admin links, or anything that should stay on the origin.
	 *
	 * @param string $html Full HTML output.
	 * @return string Rewritten HTML.
	 */
	public function rewrite( string $html ): string {
		if ( empty( $html ) ) {
			return $html;
		}

		$site    = $this->site_url;
		$cdn     = $this->cdn_url;
		$search  = [];
		$replace = [];

		// Rewrite both http and https origin URLs regardless of what the site uses.
		foreach ( [ 'https://', 'http://' ] as $scheme ) {
			$origin_base = $scheme . preg_replace( '#^https?://#', '', $site );

			$search[]  = $origin_base . '/wp-content/';
			$replace[] = $cdn . '/wp-content/';

			$search[]  = $origin_base . '/wp-includes/';
			$replace[] = $cdn . '/wp-includes/';
		}

		return str_replace( $search, $replace, $html );
	}
}
