<?php
/**
 * Conflict Detector
 *
 * Detects conflicts between WP PowerSuite modules. Modules can declare
 * conflicts in meta.json via a "conflicts" array of module IDs. When
 * enabling a module, conflicting enabled modules are detected and reported.
 *
 * @package WP_PowerSuite\Core
 * @since 1.0.0
 */

namespace WP_PowerSuite\Core;

/**
 * Conflict Detector Class
 *
 * Provides conflict detection for all modules based on metadata (meta.json).
 *
 * @since 1.0.0
 */
class ConflictDetector {

	/**
	 * Conflict Detector Instance
	 *
	 * @var ConflictDetector|null
	 * @since 1.0.0
	 */
	private static ?ConflictDetector $instance = null;

	/**
	 * Module Registry
	 *
	 * @var ModuleRegistry|null
	 * @since 1.0.0
	 */
	private ?ModuleRegistry $module_registry = null;

	/**
	 * Get Conflict Detector Instance
	 *
	 * @since 1.0.0
	 * @return ConflictDetector
	 */
	public static function getInstance(): ConflictDetector {
		if ( self::$instance === null ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor
	 *
	 * @since 1.0.0
	 */
	private function __construct() {
		// Private for singleton
	}

	/**
	 * Get module IDs that conflict with the given module (from meta.json).
	 *
	 * @since 1.0.0
	 * @param string $module_id Module ID.
	 * @return array<string> List of conflicting module IDs.
	 */
	public function getConflictsForModule( string $module_id ): array {
		$registry = $this->getRegistry();
		$registry->init();
		$meta = $registry->getModuleMetadata( $module_id );
		if ( empty( $meta ) || empty( $meta['conflicts'] ) || ! is_array( $meta['conflicts'] ) ) {
			return [];
		}
		return $meta['conflicts'];
	}

	/**
	 * Get enabled module IDs that conflict with the given module.
	 * Use this before enabling a module to see if any already-enabled module conflicts.
	 *
	 * @since 1.0.0
	 * @param string   $module_id    Module to enable.
	 * @param array<string> $enabled_list Currently enabled module IDs.
	 * @return array<string> Enabled module IDs that conflict with $module_id.
	 */
	public function getConflictsWithEnabled( string $module_id, array $enabled_list ): array {
		$conflicts = $this->getConflictsForModule( $module_id );
		if ( empty( $conflicts ) ) {
			return [];
		}
		$enabled_set = array_flip( $enabled_list );
		$blocking = [];
		foreach ( $conflicts as $conflict_id ) {
			if ( isset( $enabled_set[ $conflict_id ] ) ) {
				$blocking[] = $conflict_id;
			}
		}
		return $blocking;
	}

	/**
	 * Enabled modules that block enabling or loading the given module (forward + reverse).
	 *
	 * @since 1.0.0
	 * @param string        $module_id    Module to enable or load.
	 * @param array<string> $enabled_list Currently enabled module IDs.
	 * @return array<string> Blocking enabled module IDs.
	 */
	public function getBlockingConflictsForEnable( string $module_id, array $enabled_list ): array {
		$blocking = $this->getConflictsWithEnabled( $module_id, $enabled_list );

		foreach ( $enabled_list as $enabled_id ) {
			if ( $enabled_id === $module_id ) {
				continue;
			}

			if ( in_array( $module_id, $this->getConflictsForModule( $enabled_id ), true ) ) {
				$blocking[] = $enabled_id;
			}
		}

		$blocking = array_merge(
			$blocking,
			ApplicationPasswordConflictPolicy::getBlockingModulesForEnable( $module_id, $enabled_list )
		);

		return array_values( array_unique( $blocking ) );
	}

	/**
	 * Check if enabling the given module would conflict with currently enabled modules.
	 *
	 * @since 1.0.0
	 * @param string         $module_id    Module to enable.
	 * @param array<string>  $enabled_list Currently enabled module IDs.
	 * @return bool True if enabling would cause a conflict.
	 */
	public function wouldBlockEnable( string $module_id, array $enabled_list ): bool {
		return count( $this->getBlockingConflictsForEnable( $module_id, $enabled_list ) ) > 0;
	}

	/**
	 * Get all conflict pairs for currently enabled modules.
	 * Returns an array keyed by module_id with values being arrays of conflicting enabled module IDs.
	 *
	 * @since 1.0.0
	 * @param array<string> $enabled_list Currently enabled module IDs.
	 * @return array<string, array<string>> [ module_id => [ conflicting_enabled_id, ... ], ... ]
	 */
	public function getActiveConflicts( array $enabled_list ): array {
		$active = [];
		foreach ( $enabled_list as $module_id ) {
			$blocking = $this->getBlockingConflictsForEnable( $module_id, $enabled_list );
			if ( ! empty( $blocking ) ) {
				$active[ $module_id ] = $blocking;
			}
		}
		return $active;
	}

	/**
	 * Get a full conflict map for all modules (for admin UI).
	 * Key: module ID, Value: array of conflicting module IDs from meta.json.
	 *
	 * @since 1.0.0
	 * @return array<string, array<string>>
	 */
	public function getAllConflictsMap(): array {
		$registry = $this->getRegistry();
		$registry->init();
		$all = $registry->getAllModules();
		$map = [];
		foreach ( $all as $module_id => $meta ) {
			$conflicts = ! empty( $meta['conflicts'] ) && is_array( $meta['conflicts'] )
				? $meta['conflicts']
				: [];
			$map[ $module_id ] = $conflicts;
		}
		return $map;
	}

	/**
	 * Get Module Registry instance.
	 *
	 * @since 1.0.0
	 * @return ModuleRegistry
	 */
	private function getRegistry(): ModuleRegistry {
		if ( $this->module_registry === null ) {
			$this->module_registry = ModuleRegistry::getInstance();
		}
		return $this->module_registry;
	}
}
