<?php
/**
 * Sistema de hooks compatible con WordPress (add_action, add_filter, etc.).
 */

final class JB_Hook_System {
	private static $actions = array();
	private static $filters = array();
	private static $current_filter = array();

	public static function add_action( $tag, $callback, $priority = 10, $accepted_args = 1 ) {
		self::add_filter( $tag, $callback, $priority, $accepted_args );
	}

	public static function add_filter( $tag, $callback, $priority = 10, $accepted_args = 1 ) {
		if ( ! isset( self::$filters[ $tag ] ) ) {
			self::$filters[ $tag ] = array();
		}
		if ( ! isset( self::$filters[ $tag ][ $priority ] ) ) {
			self::$filters[ $tag ][ $priority ] = array();
		}
		self::$filters[ $tag ][ $priority ][] = array(
			'callback'        => $callback,
			'accepted_args'   => (int) $accepted_args,
		);
	}

	public static function remove_action( $tag, $callback, $priority = 10 ) {
		self::remove_filter( $tag, $callback, $priority );
	}

	public static function remove_filter( $tag, $callback, $priority = 10 ) {
		if ( ! isset( self::$filters[ $tag ][ $priority ] ) ) {
			return false;
		}
		foreach ( self::$filters[ $tag ][ $priority ] as $i => $hook ) {
			if ( self::callbacks_match( $hook['callback'], $callback ) ) {
				unset( self::$filters[ $tag ][ $priority ][ $i ] );
				return true;
			}
		}
		return false;
	}

	public static function do_action( $tag, ...$args ) {
		self::apply_filters( $tag, null, ...$args );
	}

	public static function apply_filters( $tag, $value, ...$args ) {
		self::$current_filter[] = $tag;

		if ( ! isset( self::$filters[ $tag ] ) ) {
			array_pop( self::$current_filter );
			return $value;
		}

		ksort( self::$filters[ $tag ] );
		foreach ( self::$filters[ $tag ] as $priority => $hooks ) {
			foreach ( $hooks as $hook ) {
				$cb_args = array_slice( array_merge( array( $value ), $args ), 0, $hook['accepted_args'] );
				$result  = call_user_func_array( $hook['callback'], $cb_args );
				if ( 'init' !== $tag && 'plugins_loaded' !== $tag && func_num_args() > 1 ) {
					$value = $result;
				}
			}
		}

		array_pop( self::$current_filter );
		return $value;
	}

	public static function has_action( $tag, $callback = false ) {
		return self::has_filter( $tag, $callback );
	}

	public static function has_filter( $tag, $callback = false ) {
		if ( ! isset( self::$filters[ $tag ] ) ) {
			return false;
		}
		if ( false === $callback ) {
			return true;
		}
		foreach ( self::$filters[ $tag ] as $hooks ) {
			foreach ( $hooks as $hook ) {
				if ( self::callbacks_match( $hook['callback'], $callback ) ) {
					return true;
				}
			}
		}
		return false;
	}

	private static function callbacks_match( $a, $b ) {
		if ( $a === $b ) {
			return true;
		}
		if ( is_array( $a ) && is_array( $b ) && count( $a ) === 2 && count( $b ) === 2 ) {
			return $a[0] === $b[0] && $a[1] === $b[1];
		}
		return false;
	}
}

function add_action( $tag, $callback, $priority = 10, $accepted_args = 1 ) {
	JB_Hook_System::add_action( $tag, $callback, $priority, $accepted_args );
}

function add_filter( $tag, $callback, $priority = 10, $accepted_args = 1 ) {
	JB_Hook_System::add_filter( $tag, $callback, $priority, $accepted_args );
}

function remove_action( $tag, $callback, $priority = 10 ) {
	return JB_Hook_System::remove_action( $tag, $callback, $priority );
}

function remove_filter( $tag, $callback, $priority = 10 ) {
	return JB_Hook_System::remove_filter( $tag, $callback, $priority );
}

function do_action( $tag, ...$args ) {
	JB_Hook_System::do_action( $tag, ...$args );
}

function apply_filters( $tag, $value, ...$args ) {
	return JB_Hook_System::apply_filters( $tag, $value, ...$args );
}

function has_action( $tag, $callback = false ) {
	return JB_Hook_System::has_action( $tag, $callback );
}

function has_filter( $tag, $callback = false ) {
	return JB_Hook_System::has_filter( $tag, $callback );
}

function register_activation_hook( $file, $callback ) {
	// En standalone se ejecuta desde el instalador.
	JB_App::register_activation( $callback );
}
