<?php
/**
 * Module Manager
 *
 * Manages module loading, dependencies, and initialization
 *
 * @package NemtlyBooking
 * @since   PBOOKING_SINCE
 */

namespace NemtlyBooking\Core;

use NemtlyBooking\Interfaces\Module_Interface;

/**
 * Module Manager class
 *
 * @since PBOOKING_SINCE
 */
class Module_Manager {

    /**
     * Container instance
     *
     * @var Container
     */
    private $container;

    /**
     * Registered modules
     *
     * @var Module_Interface[]
     */
    private $modules = [];

    /**
     * Initialized modules
     *
     * @var Module_Interface[]
     */
    private $initialized_modules = [];

    /**
     * Constructor
     *
     * @since PBOOKING_SINCE
     *
     * @param Container $container The dependency injection container.
     */
    public function __construct( Container $container ) {
        $this->container = $container;
    }

    /**
     * Register a module
     *
     * @since PBOOKING_SINCE
     *
     * @param Module_Interface $module Module instance.
     */
    public function register_module( Module_Interface $module ) {
        $module_name                   = $module->get_name();
        $this->modules[ $module_name ] = $module;
    }

    /**
     * Initialize all registered modules
     *
     * @since PBOOKING_SINCE
     */
    public function initialize_modules() {
        // Sort modules by dependencies
        $sorted_modules = $this->sort_modules_by_dependencies();

        foreach ( $sorted_modules as $module ) {
            if ( $module->is_enabled() ) {
                $this->initialize_module( $module );
            }
        }
    }

    /**
     * Initialize a single module
     *
     * @since PBOOKING_SINCE
     *
     * @param Module_Interface $module Module to initialize.
     */
    private function initialize_module( Module_Interface $module ) {
        $module_name = $module->get_name();

        // Check if already initialized
        if ( isset( $this->initialized_modules[ $module_name ] ) ) {
            return;
        }

        // Check dependencies
        $dependencies = $module->get_dependencies();
        foreach ( $dependencies as $dependency ) {
            if ( ! isset( $this->initialized_modules[ $dependency ] ) ) {
                // Dependency not initialized, skip this module for now
                return;
            }
        }

        // Initialize the module
        $module->init();
        $this->initialized_modules[ $module_name ] = $module;
    }

    /**
     * Sort modules by dependencies using topological sort
     *
     * @since PBOOKING_SINCE
     *
     * @return Module_Interface[] Sorted modules.
     */
    private function sort_modules_by_dependencies() {
        $sorted   = [];
        $visited  = [];
        $visiting = [];

        foreach ( $this->modules as $module ) {
            $this->visit_module( $module, $visited, $visiting, $sorted );
        }

        return $sorted;
    }

    /**
     * Visit a module for topological sorting
     *
     * @since PBOOKING_SINCE
     *
     * @param Module_Interface $module   Module to visit.
     * @param array            $visited  Already visited modules.
     * @param array            $visiting Currently visiting modules.
     * @param array            $sorted   Sorted modules.
     */
    private function visit_module( Module_Interface $module, &$visited, &$visiting, &$sorted ) {
        $module_name = $module->get_name();

        if ( isset( $visited[ $module_name ] ) ) {
            return;
        }

        if ( isset( $visiting[ $module_name ] ) ) {
            // Circular dependency detected
            return;
        }

        $visiting[ $module_name ] = true;

        $dependencies = $module->get_dependencies();
        foreach ( $dependencies as $dependency ) {
            if ( isset( $this->modules[ $dependency ] ) ) {
                $this->visit_module( $this->modules[ $dependency ], $visited, $visiting, $sorted );
            }
        }

        unset( $visiting[ $module_name ] );
        $visited[ $module_name ] = true;
        $sorted[]                = $module;
    }

    /**
     * Get all registered modules
     *
     * @since PBOOKING_SINCE
     *
     * @return Module_Interface[]
     */
    public function get_modules() {
        return $this->modules;
    }

    /**
     * Get all initialized modules
     *
     * @since PBOOKING_SINCE
     *
     * @return Module_Interface[]
     */
    public function get_initialized_modules() {
        return $this->initialized_modules;
    }

    /**
     * Get a specific module
     *
     * @since PBOOKING_SINCE
     *
     * @param string $module_name Module name.
     *
     * @return Module_Interface|null Module instance or null if not found.
     */
    public function get_module( $module_name ) {
        return $this->modules[ $module_name ] ?? null;
    }

    /**
     * Check if a module is registered
     *
     * @since PBOOKING_SINCE
     *
     * @param string $module_name Module name.
     *
     * @return bool True if registered, false otherwise.
     */
    public function is_module_registered( $module_name ) {
        return isset( $this->modules[ $module_name ] );
    }

    /**
     * Check if a module is initialized
     *
     * @since PBOOKING_SINCE
     *
     * @param string $module_name Module name.
     *
     * @return bool True if initialized, false otherwise.
     */
    public function is_module_initialized( $module_name ) {
        return isset( $this->initialized_modules[ $module_name ] );
    }
}
