<?php

declare(strict_types=1);

namespace SecurWP\Includes;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use SecurWP\Includes\Interfaces\Module;

/**
 * Collects module hook declarations before registering them with WordPress.
 */
class Module_Loader {

	/**
	 * @var array<int,array{hook: string, component: mixed, callback: mixed, priority: int, accepted_args: int}>
	 */
	protected array $actions;

	/**
	 * @var array<int,array{hook: string, component: mixed, callback: mixed, priority: int, accepted_args: int}>
	 */
	protected array $filters;

	/**
	 * @var array<string,Module>
	 */
	protected array $modules;

	public function __construct() {
		$this->actions = [];
		$this->filters = [];
		$this->modules = [];
	}

	public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ): void {
		$this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args );
	}

	public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ): void {
		$this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args );
	}

	/**
	 * @param array<int,array{hook: string, component: mixed, callback: mixed, priority: int, accepted_args: int}> $hooks
	 * @return array<int,array{hook: string, component: mixed, callback: mixed, priority: int, accepted_args: int}>
	 */
	private function add( array $hooks, $hook, $component, $callback, $priority, $accepted_args ): array {
		$hooks[] = [
			'hook'          => $hook,
			'component'     => $component,
			'callback'      => $callback,
			'priority'      => $priority,
			'accepted_args' => $accepted_args,
		];

		return $hooks;
	}

	public function load_modules( $plugin_name, $version ): void {
		$defined_modules = [
			'Scanner'       => '\SecurWP\Modules\Scanner\Scanner',
			'Firewall'      => '\SecurWP\Modules\Firewall\Firewall',
			'Hardening'     => '\SecurWP\Modules\Hardening\Hardening',
			'Vulnerability' => '\SecurWP\Modules\Vulnerability\Vulnerability',
			'Notifications' => '\SecurWP\Modules\Notifications\Notifications',
			'Cloudflare'    => '\SecurWP\Modules\Cloudflare\Cloudflare',
			'Login'         => '\SecurWP\Modules\Login\Login',
			'AuditLog'      => '\SecurWP\Modules\AuditLog\AuditLog',
			'Reporting'     => '\SecurWP\Modules\Reporting\Reporting',
			'Settings'      => '\SecurWP\Modules\Settings\Settings',
			'Cloud'         => '\SecurWP\Modules\Cloud\Cloud',
			'WhiteLabel'    => '\SecurWP\Modules\WhiteLabel\WhiteLabel',
			'TwoFactor'     => '\SecurWP\Modules\TwoFactor\TwoFactor',
			'Headers'       => '\SecurWP\Modules\Headers\Headers',

			'Multisite'     => '\SecurWP\Modules\Multisite\Multisite',
			'Tools'         => '\SecurWP\Modules\Tools\Tools',
		];

		foreach ( $defined_modules as $module_key => $module_class_name ) {
			if ( class_exists( $module_class_name ) ) {
				try {
					$module_instance = new $module_class_name( $plugin_name, $version );

					if ( $module_instance instanceof Module ) {
						$module_instance->initialize( $this );
						$this->modules[ $module_key ] = $module_instance;
					} else {
						securwp_log( sprintf( 'SecurWP Error: Module %s does not implement the Module interface.', $module_class_name ) );
					}
				} catch ( \Throwable $e ) {
					securwp_log( sprintf( 'SecurWP Error: Module %s failed to initialize: %s', $module_key, $e->getMessage() ) );
				}
			} else {
				securwp_log( "SecurWP Debug: Class {$module_class_name} not found." );
			}
		}
	}

	/**
	 * Retrieve a loaded module by its key.
	 *
	 * @param string $key Module key (e.g. 'Scanner', 'Cloud').
	 * @return Module|null The module instance, or null if not loaded.
	 */
	public function get_module( string $key ): ?Module {
		return $this->modules[ $key ] ?? null;
	}

	public function run(): void {
		foreach ( $this->filters as $hook_details ) {
			$callback_to_register = $hook_details['component'] === null
				? $hook_details['callback']
				: [ $hook_details['component'], $hook_details['callback'] ];

			add_filter( $hook_details['hook'], $callback_to_register, $hook_details['priority'], $hook_details['accepted_args'] );
		}

		foreach ( $this->actions as $hook_details ) {
			$callback_to_register = $hook_details['component'] === null
				? $hook_details['callback']
				: [ $hook_details['component'], $hook_details['callback'] ];

			add_action( $hook_details['hook'], $callback_to_register, $hook_details['priority'], $hook_details['accepted_args'] );
		}
	}
}
