<?php
/**
 * Dequeue WooCommerce Assets module.
 *
 * @package Kramar
 * @since   1.0.0
 */

declare( strict_types=1 );

namespace Kramar\Modules\Performance;

// Prevent direct access.
defined( 'ABSPATH' ) || exit;

/**
 * Dequeue WooCommerce Assets module.
 *
 * @since 1.0.0
 */
final class Dequeue_Assets extends \Kramar\Module {

	/**
	 * {@inheritDoc}
	 */
	public function get_slug(): string {
		return 'dequeue-assets';
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_category(): string {
		return 'performance';
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_name(): string {
		return __( 'Dequeue WooCommerce Assets', 'kramar' );
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_description(): string {
		return __( 'Remove WooCommerce CSS and JS files that your theme already handles. Reduces page weight on every page load.', 'kramar' );
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_default_settings(): array {
		return [
			'enabled'               => false,
			'dequeue_css'           => false,
			'dequeue_block_css'     => false,
			'dequeue_js'            => false,
			'keep_cart_checkout_js' => true,
			'non_shop_only'         => false,
		];
	}

	/**
	 * {@inheritDoc}
	 */
	public function sanitize_settings( array $input ): array {
		return [
			'enabled'               => ! empty( $input['enabled'] ),
			'dequeue_css'           => ! empty( $input['dequeue_css'] ),
			'dequeue_block_css'     => ! empty( $input['dequeue_block_css'] ),
			'dequeue_js'            => ! empty( $input['dequeue_js'] ),
			'keep_cart_checkout_js' => ! empty( $input['keep_cart_checkout_js'] ),
			'non_shop_only'         => ! empty( $input['non_shop_only'] ),
		];
	}

	/**
	 * {@inheritDoc}
	 */
	protected function run(): void {
		add_action( 'wp_enqueue_scripts', [ $this, 'dequeue_assets' ], 99 );

		// Blocks CSS is handled separately, by filtering the list of handles at
		// the moment they print. WooCommerce ships one stylesheet per block and
		// enqueues it from the block's own render_callback(), so they appear
		// after wp_enqueue_scripts has finished — some during the template
		// render, some late enough to print in the footer. This filter runs on
		// every print pass, head and footer alike, which is the only place that
		// sees all of them.
		add_filter( 'print_styles_array', [ $this, 'filter_block_styles' ] );
	}

	/**
	 * Dequeue WooCommerce styles and scripts based on settings.
	 *
	 * @since 1.0.0
	 */
	public function dequeue_assets(): void {
		$non_shop_only = (bool) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'non_shop_only' );
		if ( $non_shop_only && $this->is_woocommerce_page() ) {
			return;
		}

		if ( (bool) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'dequeue_css' ) ) {
			wp_dequeue_style( 'woocommerce-layout' );
			wp_dequeue_style( 'woocommerce-smallscreen' );
			wp_dequeue_style( 'woocommerce-general' );
			wp_dequeue_style( 'woocommerce-inline' );

			// Only dequeue blocktheme CSS on classic themes — block themes need it.
			if ( ! wp_is_block_theme() ) {
				wp_dequeue_style( 'woocommerce-blocktheme' );
			}
		}

		if ( (bool) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'dequeue_js' ) ) {
			$keep_functional = (bool) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'keep_cart_checkout_js' );

			// Not dequeued when cart/checkout JS is being kept: wc-cart,
			// wc-checkout, wc-add-payment-method and wc-lost-password all declare
			// 'woocommerce' as a dependency, so WordPress reprints it anyway and
			// the call is a no-op that only makes the setting look like it does
			// less than it says.
			if ( ! $keep_functional ) {
				wp_dequeue_script( 'woocommerce' );
			}

			wp_dequeue_script( 'wc-password-strength-meter' );
			wp_dequeue_script( 'selectWoo' );
			wp_dequeue_script( 'select2' );

			if ( ! $keep_functional ) {
				wp_dequeue_script( 'wc-cart' );
				wp_dequeue_script( 'wc-checkout' );
				wp_dequeue_script( 'wc-add-to-cart' );
				wp_dequeue_script( 'wc-single-product' );
			}
		}
	}

	/**
	 * Drop WooCommerce Blocks stylesheets from the list about to be printed.
	 *
	 * This used to dequeue two fixed handles on wp_enqueue_scripts. One of them,
	 * wc-blocks-vendors-style, is not a stylesheet in any current WooCommerce,
	 * so it never did anything; the other is the base file only. WooCommerce
	 * ships one stylesheet per block — wc-blocks-style-mini-cart,
	 * wc-blocks-style-product-image and dozens more — enqueued as each block
	 * renders, which is too late for that hook. A shop page lost 14KB and kept
	 * over 100KB, from a setting that exists to remove exactly those files.
	 *
	 * Matching by prefix means new blocks are covered without this file having
	 * to stay in step with WooCommerce.
	 *
	 * @since 1.0.2
	 *
	 * @param array $handles Style handles queued for this print pass.
	 * @return array Handles with the WooCommerce Blocks stylesheets removed.
	 */
	public function filter_block_styles( $handles ) {
		if ( ! is_array( $handles ) || ! $handles ) {
			return $handles;
		}

		// Front end only. The hook this replaces was wp_enqueue_scripts, which
		// never fires in wp-admin; print_styles_array does, and the block editor
		// loads these same stylesheets to render WooCommerce blocks in the
		// canvas. Stripping them there would break editing, not speed anything
		// up for a visitor.
		if ( is_admin() ) {
			return $handles;
		}

		if ( ! (bool) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'dequeue_block_css' ) ) {
			return $handles;
		}

		$non_shop_only = (bool) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'non_shop_only' );
		if ( $non_shop_only && $this->is_woocommerce_page() ) {
			return $handles;
		}

		return array_values(
			array_filter(
				$handles,
				static fn( $handle ): bool => ! str_starts_with( (string) $handle, 'wc-blocks-' )
			)
		);
	}

	/**
	 * {@inheritDoc}
	 */
	public function render_extra_settings( string $option_name ): void {
		$dequeue_css           = (bool) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'dequeue_css' );
		$dequeue_block_css     = (bool) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'dequeue_block_css' );
		$dequeue_js            = (bool) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'dequeue_js' );
		$keep_cart_checkout_js = (bool) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'keep_cart_checkout_js' );
		$non_shop_only         = (bool) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'non_shop_only' );
		?>
		<div class="kramar-module__extra">
			<div class="kramar-settings-stack">
				<p>
					<label>
						<input
							type="checkbox"
							name="<?php echo esc_attr( $option_name ); ?>[performance][dequeue-assets][dequeue_css]"
							value="1"
							<?php checked( $dequeue_css ); ?>
						/>
						<?php esc_html_e( 'Remove all WooCommerce CSS', 'kramar' ); ?>
					</label>
					<span class="description"><?php esc_html_e( 'woocommerce-layout, woocommerce-smallscreen, woocommerce-general', 'kramar' ); ?></span>
				</p>
				<p>
					<label>
						<input
							type="checkbox"
							name="<?php echo esc_attr( $option_name ); ?>[performance][dequeue-assets][dequeue_block_css]"
							value="1"
							<?php checked( $dequeue_block_css ); ?>
						/>
						<?php esc_html_e( 'Remove WooCommerce Blocks CSS', 'kramar' ); ?>
					</label>
					<span class="description"><?php esc_html_e( 'Every wc-blocks-* stylesheet, including the per-block files. Leave off if your theme uses WooCommerce blocks such as the Mini Cart — they will lose their styling.', 'kramar' ); ?></span>
				</p>
				<p>
					<label>
						<input
							type="checkbox"
							name="<?php echo esc_attr( $option_name ); ?>[performance][dequeue-assets][dequeue_js]"
							value="1"
							<?php checked( $dequeue_js ); ?>
						/>
						<?php esc_html_e( 'Remove non-essential WooCommerce JS', 'kramar' ); ?>
					</label>
					<span class="description"><?php esc_html_e( 'woocommerce, wc-password-strength-meter, selectWoo, select2', 'kramar' ); ?></span>
				</p>
				<p>
					<label>
						<input
							type="checkbox"
							name="<?php echo esc_attr( $option_name ); ?>[performance][dequeue-assets][keep_cart_checkout_js]"
							value="1"
							<?php checked( $keep_cart_checkout_js ); ?>
						/>
						<?php esc_html_e( 'Always keep cart/checkout JS functional', 'kramar' ); ?>
					</label>
					<span class="description"><?php esc_html_e( 'Only applies when JS removal is on. Preserves wc-cart, wc-checkout, wc-add-to-cart, wc-single-product.', 'kramar' ); ?></span>
				</p>
				<p>
					<label>
						<input
							type="checkbox"
							name="<?php echo esc_attr( $option_name ); ?>[performance][dequeue-assets][non_shop_only]"
							value="1"
							<?php checked( $non_shop_only ); ?>
						/>
						<?php esc_html_e( 'Only dequeue on non-WooCommerce pages', 'kramar' ); ?>
					</label>
					<span class="description"><?php esc_html_e( 'Skips dequeuing on shop, product, cart, checkout, and account pages.', 'kramar' ); ?></span>
				</p>
			</div>
		</div>
		<?php
	}

	/**
	 * Check if the current page is a WooCommerce page.
	 *
	 * @since 1.0.0
	 *
	 * @return bool
	 */
	private function is_woocommerce_page(): bool {
		return is_woocommerce() || is_cart() || is_checkout() || is_account_page();
	}
}
