<?php

namespace TotalTheme\Helpers;

defined( 'ABSPATH' ) || exit;

/**
 * Class Remove_Class_Action
 *
 * Allows removing class-based actions from WordPress hooks when the object instance
 * is not accessible. Efficiently queues multiple class actions per hook and processes
 * them in a single low-priority callback.
 */
class Remove_Class_Action {

	/**
	 * Multi-dimensional array of queued actions.
	 *
	 * Format:
	 * [
	 *   'hook_name' => [
	 *       [
	 *           'class'    => 'ClassName',
	 *           'method'   => 'methodName',
	 *           'priority' => 10,
	 *       ],
	 *   ],
	 * ]
	 *
	 * @var array
	 */
	protected static array $actions_to_remove = [];

	/**
	 * Keeps track of hooks that already have a removal callback attached.
	 *
	 * @var array
	 */
	protected static array $hooked_hooks = [];

	/**
	 * Queue a class action for removal on a specific hook.
	 *
	 * @param string $hook_name
	 * @param string $class_name
	 * @param string $method_name
	 * @param int    $priority
	 */
	public static function queue( string $hook_name, string $class_name, string $method_name, int $priority = 10 ): void {

		// Add action to the queue for this hook
		self::$actions_to_remove[ $hook_name ][] = [
			'class'    => $class_name,
			'method'   => $method_name,
			'priority' => $priority,
		];

		// Only add one removal callback per hook
		if ( ! in_array( $hook_name, self::$hooked_hooks, true ) ) {
			add_action( $hook_name, function() use ( $hook_name ) {
				self::process_hook( $hook_name );
			}, -10 );
			self::$hooked_hooks[] = $hook_name;
		}
	}

	/**
	 * Removes all queued class actions for the current hook.
	 */
	public static function process_hook( $hook_name ): void {
		global $wp_filter;

		if ( ! isset( self::$actions_to_remove[ $hook_name ] ) ) {
			return;
		}

		foreach ( self::$actions_to_remove[ $hook_name ] as $action ) {
			$class_name  = $action['class'];
			$method_name = $action['method'];
			$priority    = $action['priority'];

			if ( ! isset( $wp_filter[ $hook_name ][ $priority ] ) ) {
				continue;
			}

			foreach ( (array) $wp_filter[ $hook_name ][ $priority ] as $id => $filter ) {

				if ( isset( $filter['function'] ) && is_array( $filter['function'] ) ) {
					$obj    = $filter['function'][0] ?? null;
					$method = $filter['function'][1] ?? null;

					if ( is_object( $obj ) && get_class( $obj ) === $class_name && $method === $method_name ) {
						if ( is_object( $wp_filter[ $hook_name ] ) && isset( $wp_filter[ $hook_name ]->callbacks[ $priority ][ $id ] ) ) {
							unset( $wp_filter[ $hook_name ]->callbacks[ $priority ][ $id ] );
						} elseif ( isset( $wp_filter[ $hook_name ][ $priority ][ $id ] ) ) {
							unset( $wp_filter[ $hook_name ][ $priority ][ $id ] );
						}
					}
				}
			}
		}

		// Clear the queue for this hook after processing
		unset( self::$actions_to_remove[ $hook_name ] );
	}
}
