<?php
/**
 * Event manager for internal plugin events
 *
 * Enhanced with comprehensive observer pattern implementation
 *
 * @package NemtlyBooking
 * @since   PBOOKING_SINCE
 */

namespace NemtlyBooking\Core;

/**
 * Event Manager class
 *
 * Enhanced event manager with comprehensive observer pattern support
 *
 * @since PBOOKING_SINCE
 */
class Event_Manager {

    /**
     * Event listeners
     *
     * @var array
     */
    private $listeners = [];

    /**
     * Event subscribers
     *
     * @var array
     */
    private $subscribers = [];

    /**
     * Event history
     *
     * @var array
     */
    private $event_history = [];

    /**
     * Maximum history size
     *
     * @var int
     */
    private $max_history_size = 100;

    /**
     * Event middleware
     *
     * @var array
     */
    private $middleware = [];

    /**
     * Add event listener
     *
     * @param string   $event    Event name.
     * @param callable $callback Callback function.
     * @param int      $priority Priority (default 10).
     */
    public function add_listener( $event, $callback, $priority = 10 ) {
        $this->listeners[ $event ][ $priority ][] = $callback;
        ksort( $this->listeners[ $event ] );
    }


    /**
     * Remove event listener
     *
     * @param string   $event    Event name.
     * @param callable $callback Callback function.
     */
    public function remove_listener( $event, $callback ) {
        if ( ! isset( $this->listeners[ $event ] ) ) {
            return;
        }
        foreach ( $this->listeners[ $event ] as $priority => $callbacks ) {
            foreach ( $callbacks as $index => $registered_callback ) {
                if ( $registered_callback === $callback ) {
                    unset( $this->listeners[ $event ][ $priority ][ $index ] );
                }
            }
        }
    }

    /**
     * Register event subscriber
     *
     * @since PBOOKING_SINCE
     *
     * @param string $subscriber_class Subscriber class name.
     * @param object $subscriber       Subscriber instance.
     */
    public function register_subscriber( $subscriber_class, $subscriber ) {
        $this->subscribers[ $subscriber_class ] = $subscriber;

        // Auto-register subscriber methods
        if ( method_exists( $subscriber, 'get_subscribed_events' ) ) {
            $events = $subscriber->get_subscribed_events();
            foreach ( $events as $event => $method ) {
                $this->add_listener( $event, [ $subscriber, $method ] );
            }
        }
    }

    /**
     * Unregister event subscriber
     *
     * @since PBOOKING_SINCE
     *
     * @param string $subscriber_class Subscriber class name.
     */
    public function unregister_subscriber( $subscriber_class ) {
        if ( isset( $this->subscribers[ $subscriber_class ] ) ) {
            $subscriber = $this->subscribers[ $subscriber_class ];

            // Remove all listeners for this subscriber
            if ( method_exists( $subscriber, 'get_subscribed_events' ) ) {
                $events = $subscriber->get_subscribed_events();
                foreach ( $events as $event => $method ) {
                    $this->remove_listener( $event, [ $subscriber, $method ] );
                }
            }

            unset( $this->subscribers[ $subscriber_class ] );
        }
    }

    /**
     * Add event middleware
     *
     * @since PBOOKING_SINCE
     *
     * @param string   $event    Event name.
     * @param callable $middleware Middleware function.
     * @param int      $priority  Priority (default 10).
     */
    public function add_middleware( $event, $middleware, $priority = 10 ) {
        $this->middleware[ $event ][ $priority ][] = $middleware;
        ksort( $this->middleware[ $event ] );
    }

    /**
     * Remove event middleware
     *
     * @since PBOOKING_SINCE
     *
     * @param string   $event      Event name.
     * @param callable $middleware Middleware function.
     */
    public function remove_middleware( $event, $middleware ) {
        if ( ! isset( $this->middleware[ $event ] ) ) {
            return;
        }
        foreach ( $this->middleware[ $event ] as $priority => $middlewares ) {
            foreach ( $middlewares as $index => $registered_middleware ) {
                if ( $registered_middleware === $middleware ) {
                    unset( $this->middleware[ $event ][ $priority ][ $index ] );
                }
            }
        }
    }

    /**
     * Dispatch event with enhanced features
     *
     * @since PBOOKING_SINCE
     *
     * @param string $event Event name.
     * @param array  $data  Event data.
     *
     * @return array Event data after processing.
     */
    public function dispatch( $event, $data = [] ) {
        // Record event in history
        $this->record_event( $event, $data );

        // Apply middleware
        $data = $this->apply_middleware( $event, $data );

        // Execute listeners
        if ( isset( $this->listeners[ $event ] ) ) {
            foreach ( $this->listeners[ $event ] as $priority => $callbacks ) {
                foreach ( $callbacks as $callback ) {
                    try {
                        $data = call_user_func( $callback, $data );
                    } catch ( \Exception $e ) {
                        error_log( $e->getMessage() );
                    }
                }
            }
        }

        return $data;
    }

    /**
     * Apply middleware to event data
     *
     * @since PBOOKING_SINCE
     *
     * @param string $event Event name.
     * @param array  $data  Event data.
     *
     * @return array Processed event data.
     */
    private function apply_middleware( $event, $data ) {
        if ( ! isset( $this->middleware[ $event ] ) ) {
            return $data;
        }

        foreach ( $this->middleware[ $event ] as $priority => $middlewares ) {
            foreach ( $middlewares as $middleware ) {
                try {
                    $data = call_user_func( $middleware, $data );
                } catch ( \Exception $e ) {
                    error_log( $e->getMessage() );
                }
            }
        }

        return $data;
    }

    /**
     * Record event in history
     *
     * @since PBOOKING_SINCE
     *
     * @param string $event Event name.
     * @param array  $data  Event data.
     */
    private function record_event( $event, $data ) {
        $this->event_history[] = [
            'event'     => $event,
            'data'      => $data,
            'timestamp' => current_time( 'mysql' ),
        ];

        // Limit history size
        if ( count( $this->event_history ) > $this->max_history_size ) {
            array_shift( $this->event_history );
        }
    }

    /**
     * Get event history
     *
     * @since PBOOKING_SINCE
     *
     * @param string $event Optional event name to filter by.
     *
     * @return array Event history.
     */
    public function get_event_history( $event = null ) {
        if ( $event ) {
            return array_filter(
                $this->event_history,
                function ( $record ) use ( $event ) {
					return $record['event'] === $event;
				}
            );
        }

        return $this->event_history;
    }

    /**
     * Clear event history
     *
     * @since PBOOKING_SINCE
     */
    public function clear_event_history() {
        $this->event_history = [];
    }

    /**
     * Get all registered listeners
     *
     * @since PBOOKING_SINCE
     *
     * @return array All listeners.
     */
    public function get_listeners() {
        return $this->listeners;
    }

    /**
     * Get listeners for specific event
     *
     * @since PBOOKING_SINCE
     *
     * @param string $event Event name.
     *
     * @return array Event listeners.
     */
    public function get_event_listeners( $event ) {
        return $this->listeners[ $event ] ?? [];
    }

    /**
     * Get all registered subscribers
     *
     * @since PBOOKING_SINCE
     *
     * @return array All subscribers.
     */
    public function get_subscribers() {
        return $this->subscribers;
    }

    /**
     * Check if event has listeners
     *
     * @since PBOOKING_SINCE
     *
     * @param string $event Event name.
     *
     * @return bool True if has listeners, false otherwise.
     */
    public function has_listeners( $event ) {
        return ! empty( $this->listeners[ $event ] );
    }

    /**
     * Get event statistics
     *
     * @since PBOOKING_SINCE
     *
     * @return array Event statistics.
     */
    public function get_statistics() {
        $stats = [
            'total_events'      => count( $this->event_history ),
            'unique_events'     => count( array_unique( array_column( $this->event_history, 'event' ) ) ),
            'listeners_count'   => 0,
            'subscribers_count' => count( $this->subscribers ),
        ];

        foreach ( $this->listeners as $event => $priorities ) {
            foreach ( $priorities as $callbacks ) {
                $stats['listeners_count'] += count( $callbacks );
            }
        }

        return $stats;
    }

    /**
     * Dispatch event asynchronously (for future implementation)
     *
     * @since PBOOKING_SINCE
     *
     * @param string $event Event name.
     * @param array  $data  Event data.
     */
    public function dispatch_async( $event, $data = [] ) {
        // For now, just dispatch normally
        // In the future, this could use WordPress cron or background processing
        $this->dispatch( $event, $data );
    }

    /**
     * Create event listener for WordPress hooks
     *
     * @since PBOOKING_SINCE
     *
     * @param string $wp_hook WordPress hook name.
     * @param string $event   Internal event name.
     */
    public function create_wp_hook_listener( $wp_hook, $event ) {
        add_action(
            $wp_hook,
            function ( ...$args ) use ( $event ) {
				$data = [
					'wp_hook' => $wp_hook,
					'args'    => $args,
				];
				$this->dispatch( $event, $data );
			}
        );
    }

    /**
     * Create event listener for WordPress filters
     *
     * @since PBOOKING_SINCE
     *
     * @param string $wp_filter WordPress filter name.
     * @param string $event     Internal event name.
     */
    public function create_wp_filter_listener( $wp_filter, $event ) {
        add_filter(
            $wp_filter,
            function ( $value, ...$args ) use ( $event ) {
				$data = [
					'wp_filter' => $wp_filter,
					'value'     => $value,
					'args'      => $args,
				];
				$this->dispatch( $event, $data );
				return $value;
			},
            10,
            999
        );
    }
}
