<?php
/**
 * Assets class.
 *
 * @package DokanKits\Admin
 * @since   3.0.0
 */

namespace DokanKits\Admin;

use _WP_Dependency;
use DokanKits\Core\Interfaces\Hookable;
use Throwable;
use WP_Screen;
use WP_Scripts;
use WP_Styles;

/**
 * Assets Class
 *
 * Handles admin assets registration and enqueuing
 *
 * @since 3.0.0
 * @package DokanKits\Admin
 */
class Assets implements Hookable {

	/**
	 * Initialize assets
	 *
	 * @since 3.0.0
	 * @return void
	 */
	public function register_hooks(): void {
		/**
		 * Action before admin assets hooks are registered
		 *
		 * @since 3.0.0
		 * @param Assets $this Assets instance.
		 */
		do_action( 'dokan_kits_before_admin_assets_hooks', $this );

		add_action( 'dokan_kits_enqueue_admin_scripts', array( $this, 'register_all_scripts' ) );

		// Asset cleanup for plugin pages.
		add_action( 'admin_enqueue_scripts', array( $this, 'clean_third_party_deps' ), 999999999999 );
		add_action( 'admin_head', array( $this, 'clean_admin_content_section' ), 999999999999 );

		/**
		 * Action after admin assets hooks are registered
		 *
		 * @since 3.0.0
		 * @param Assets $this Assets instance.
		 */
		do_action( 'dokan_kits_after_admin_assets_hooks', $this );
	}

	/**
	 * Register all scripts
	 *
	 * @since 3.0.0
	 * @return void
	 */
	public function register_all_scripts(): void {
		$asset_file = plugin_dir_path( DOKAN_KITS_FILE ) . '/build/admin/app.asset.php';
		if ( ! file_exists( $asset_file ) ) {
			return;
		}

		$asset   = include $asset_file;
		$deps    = $asset['dependencies'] ?? array();
		$version = $asset['version'] ?? DOKAN_KITS_VERSION;

		/**
		 * Filter the admin script dependencies
		 *
		 * @since 3.0.0
		 * @param array $deps Script dependencies.
		 */
		$deps = apply_filters( 'dokan_kits_admin_script_deps', $deps );

		/**
		 * Filter the admin script version
		 *
		 * @since 3.0.0
		 * @param string $version Script version.
		 */
		$version = apply_filters( 'dokan_kits_admin_script_version', $version );

		// Register scripts and styles specific to this page.
		wp_register_script(
			'dokan-kits-dashboard-app',
			DOKAN_KITS_BUILD_URL . '/admin/app.js',
			$deps,
			$version,
			true
		);

		wp_register_style(
			'dokan-kits-dashboard-app',
			DOKAN_KITS_BUILD_URL . '/admin/app.css',
			array( 'wp-components' ),
			$version
		);

		// Get the current initial path.
		$initial_path = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

		// Get plugin data for front-end components.
		$plugin_data = array(
			'isDebugMode' => defined( 'WP_DEBUG' ) && defined( 'SCRIPT_DEBUG' ) && defined( 'DOKAN_KITS_DEBUG' ) && WP_DEBUG && SCRIPT_DEBUG && DOKAN_KITS_DEBUG,
			'buildUrl'    => DOKAN_KITS_BUILD_URL,
			'ajaxUrl'     => admin_url( 'admin-ajax.php' ),
			'apiRoot'     => esc_url_raw( rest_url( 'dokan-kits/v1' ) ),
			'nonce'       => wp_create_nonce( 'wp_rest' ),
			'adminUrl'    => admin_url( 'admin.php' ),
			'version'     => DOKAN_KITS_VERSION,
			'initialPath' => $initial_path,
			'pageUrls'    => array(
				'dashboard' => admin_url( 'admin.php?page=dokan-kits' ),
				'features'  => admin_url( 'admin.php?page=dokan-kits-features' ),
				'about'     => admin_url( 'admin.php?page=dokan-kits-about' ),
				'add_ons'   => dokan_kits_fs()->get_addons_url(),
			),
		);

		/**
		 * Filter admin script data
		 *
		 * @since 3.0.0
		 * @param array $plugin_data Script data.
		 */
		$plugin_data = apply_filters( 'dokan_kits_admin_script_data', $plugin_data );

		wp_localize_script( 'dokan-kits-dashboard-app', 'Dokan_Kits', $plugin_data );
		wp_set_script_translations( 'dokan-kits-dashboard-app', 'dokan-kits' );

		/**
		 * Action after admin scripts are registered
		 *
		 * @since 3.0.0
		 * @param string $handle The script handle.
		 */
		do_action( 'dokan_kits_after_admin_scripts_registered', 'dokan-kits-dashboard-app' );
	}

	/**
	 * Remove all notices from the squad template pages.
	 *
	 * @since 3.0.0
	 * @return void
	 */
	public function clean_admin_content_section(): void {
		try {
			// Check if the current screen is available.
			if ( ! function_exists( '\get_current_screen' ) ) {
				return;
			}

			$screen = get_current_screen();

			if ( ! $screen instanceof WP_Screen || ! strpos( $screen->id, 'dokan-kits' ) ) {
				return;
			}

			/**
			 * Filter whether to clean admin notices on squad pages.
			 *
			 * @since 3.0.0
			 *
			 * @param bool      $should_clean Whether to clean admin notices.
			 * @param WP_Screen $screen       Current admin screen.
			 */
			$should_clean = apply_filters( 'dokan_kits_should_clean_admin_notices', true, $screen );

			if ( ! $should_clean ) {
				return;
			}

			/**
			 * Fires before admin notices are cleaned.
			 *
			 * @since 3.0.0
			 *
			 * @param WP_Screen $screen Current admin screen.
			 */
			do_action( 'dokan_kits_before_clean_admin_notices', $screen );

			remove_all_actions( 'admin_notices' );
			remove_all_actions( 'network_admin_notices' );
			remove_all_actions( 'all_admin_notices' );
			remove_all_actions( 'user_admin_notices' );

			/**
			 * Fires after admin notices are cleaned.
			 *
			 * @since 3.0.0
			 *
			 * @param WP_Screen $screen Current admin screen.
			 */
			do_action( 'dokan_kits_after_clean_admin_notices', $screen );
		} catch ( Throwable $e ) {
			dokan_kits_logger()->error( 'Failed to clean admin content section' );
		}
	}

	/**
	 * Remove all third party dependencies from the squad template pages.
	 *
	 * @since 3.0.0
	 * @return void
	 */
	public function clean_third_party_deps(): void {
		try {
			global $wp_scripts, $wp_styles;

			// Check if the current screen is available.
			if ( ! function_exists( '\get_current_screen' ) ) {
				return;
			}

			$screen = get_current_screen();

			if ( ! $screen instanceof WP_Screen || ! strpos( $screen->id, 'dokan-kits' ) ) {
				return;
			}

			/**
			 * Filter whether to clean third party dependencies on squad pages.
			 *
			 * @since 3.0.0
			 *
			 * @param bool $should_clean Whether to clean third party dependencies.
			 */
			$should_clean = apply_filters( 'dokan_kits_should_clean_dependencies', true );

			if ( ! $should_clean ) {
				return;
			}

			/**
			 * Fires before third party dependencies are cleaned.
			 *
			 * @since 3.3.0
			 *
			 * @param WP_Scripts $wp_scripts WordPress scripts registry.
			 * @param WP_Styles  $wp_styles  WordPress styles registry.
			 */
			do_action( 'dokan_kits_before_clean_dependencies', $wp_scripts, $wp_styles );

			// Dequeue the scripts and styles of the current page that are not required.
			$this->remove_unnecessary_dependencies( $wp_scripts );
			$this->remove_unnecessary_dependencies( $wp_styles );

			/**
			 * Fires after third party dependencies are cleaned.
			 *
			 * @since 3.0.0
			 *
			 * @param WP_Scripts $wp_scripts WordPress scripts registry.
			 * @param WP_Styles  $wp_styles  WordPress styles registry.
			 */
			do_action( 'dokan_kits_after_clean_dependencies', $wp_scripts, $wp_styles );
		} catch ( Throwable $e ) {
			dokan_kits_logger()->error( 'Failed to clean third party dependencies' );
		}
	}

	/**
	 * Remove unnecessary styles from the current page.
	 *
	 * @since 3.0.0
	 * @param WP_Scripts|WP_Styles $root The Core class of dependencies.
	 *
	 * @return void
	 */
	public function remove_unnecessary_dependencies( $root ): void {
		try {
			// Get site url.
			$site_url = home_url( '/' );

			// Allowed plugin paths.
			$allowed_plugin_defaults = array( 'wp-console', 'query-monitor' );

			/**
			 * Filter the list of allowed plugin paths that won't be cleaned up.
			 *
			 * @since 3.0.0
			 *
			 * @param array                $allowed_plugin_paths The allowed plugin paths.
			 * @param WP_Scripts|WP_Styles $root                 The Core class of dependencies.
			 *
			 * @return array
			 */
			$allowed_plugin_paths = apply_filters( 'dokan_kits_dependencies_cleaning_allowed_plugin_paths', $allowed_plugin_defaults, $root );

			/**
			 * Remove all dependencies of the current page that are not required.
			 *
			 * @see https://developer.wordpress.org/reference/classes/wp_styles/
			 * @see https://developer.wordpress.org/reference/classes/wp_scripts/
			 */
			foreach ( $root->registered as $dependency ) {
				if ( ( class_exists( _WP_Dependency::class ) && ! $dependency instanceof _WP_Dependency ) || false === $dependency->src ) {
					continue;
				}

				// Check if the dependency should be dequeued and removed.
				$should_remove = strpos( $dependency->handle, 'dokan-kits-' ) !== 0 && strpos( $dependency->src, (string) $site_url ) !== false;

				// Never strip WordPress core script/style handles required by the admin app.
				if ( $should_remove && strpos( $dependency->handle, 'wp-' ) === 0 ) {
					$should_remove = false;
				}

				// Check allowed plugin paths.
				foreach ( $allowed_plugin_paths as $plugin_path ) {
					if ( strpos( $dependency->src, "wp-content/plugins/$plugin_path" ) !== false ) {
						$should_remove = false;
						break;
					}
				}

				/**
				 * Filter whether a specific dependency should be removed.
				 *
				 * @since 3.0.0
				 *
				 * @param bool                 $should_remove Whether to remove the dependency.
				 * @param _WP_Dependency       $dependency    The dependency.
				 * @param WP_Scripts|WP_Styles $root          The Core class of dependencies.
				 */
				$should_remove = apply_filters( 'dokan_kits_should_remove_dependency', $should_remove, $dependency, $root );

				// Dequeue and remove the dependency if it should be removed.
				if ( ! $should_remove ) {
					continue;
				}

				// Dequeue the dependency and remove it from the registered list.
				$root->dequeue( $dependency->handle );
				$root->remove( $dependency->handle );
			}
		} catch ( Throwable $e ) {
			dokan_kits_logger()->error( 'Failed to remove unnecessary dependencies' );
		}
	}
}
