<?php
/**
 * ServiceContainer class.
 *
 * Dependency injection container wrapper for all Firevert services.
 * Provides access to shared instances such as logger, asset loader, path resolver, etc.
 *
 * @package Ilabs\Firevert\Core
 */

declare( strict_types=1 );

namespace Ilabs\Firevert\Core;

use Ilabs\Firevert\Core\Services\Loader\AssetLoader;
use Ilabs\Firevert\Core\Services\LoggerService;
use Ilabs\Firevert\Core\Services\Resolver\PathResolver;
use Isolated\Firevert\Isolated_Pimple\Pimple\Container;

/**
 * Dependency injection container wrapper.
 */
class ServiceContainer extends Container {

	/**
	 * Retrieves a service by ID.
	 *
	 * @param string      $id          Service identifier.
	 * @param string|null $module_slug Optional module slug.
	 *
	 * @return mixed
	 */
	public function get( string $id, string $module_slug = null ) {
		$final_id = $this->resolve_service_id( $id, $module_slug );
		return $this->offsetGet( $final_id );
	}

	/**
	 * Registers a service by ID.
	 *
	 * @param string      $id          Service identifier.
	 * @param mixed       $value       Service value or closure.
	 * @param string|null $module_slug Optional module slug.
	 *
	 * @return void
	 */
	public function set( string $id, $value, string $module_slug = null ): void {
		$final_id = $this->resolve_service_id( $id, $module_slug );
		$this->offsetSet( $final_id, $value );
	}

	/**
	 * Checks if a service is defined.
	 *
	 * @param string      $id          Service identifier.
	 * @param string|null $module_slug Optional module slug.
	 *
	 * @return bool
	 */
	public function has( string $id, string $module_slug = null ): bool {
		$final_id = $this->resolve_service_id( $id, $module_slug );
		return $this->offsetExists( $final_id );
	}

	/**
	 * Removes a service from the container.
	 *
	 * @param string      $id          Service identifier.
	 * @param string|null $module_slug Optional module slug.
	 *
	 * @return void
	 */
	public function remove( string $id, string $module_slug = null ): void {
		$final_id = $this->resolve_service_id( $id, $module_slug );

		if ( $this->offsetExists( $final_id ) ) {
			$this->offsetUnset( $final_id );
		}
	}

	/**
	 * Resolves a service ID, applying module namespace if given.
	 *
	 * @param string      $id          Service identifier.
	 * @param string|null $module_slug Module slug.
	 *
	 * @return string
	 */
	private function resolve_service_id( string $id, ?string $module_slug ): string {
		return ( null !== $module_slug ) ? $module_slug . '.' . $id : $id;
	}

	/**
	 * Loads external dependency file (dependencies.php).
	 *
	 * @return void
	 */
	public function load_external_dependencies(): void {
		$path = dirname( __DIR__, 2 ) . '/dependencies.php';

		if ( file_exists( $path ) ) {
			require_once $path;
		}
	}

	/**
	 * Initializes core default services.
	 *
	 * @param array $config Plugin configuration.
	 * @param array $headers Plugin headers.
	 *
	 * @return void
	 */
	public function initialize_defaults( array $config, array $headers ): void {
		$this->set( 'config', $config );
		$this->set( 'headers', $headers );
		$this->set( 'module_manager', new ModuleManager() );

		$this->set(
			'logger',
			function () {
				return new LoggerService();
			}
		);

		$this->set(
			'path_resolver',
			function () use ( $config ) {
				$base_path      = dirname( $config['__FILE__'] );
				$base_url       = plugins_url( '/', $config['__FILE__'] );
				$module_manager = $this->get( 'module_manager' );

				return new PathResolver( $base_path, $base_url, $module_manager );
			}
		);

		$this->set(
			'asset_loader',
			function () {
				/**
				 * Get from DI container.
				 *
				 * @var PathResolver $paths
				 */
				$paths  = $this->get( 'path_resolver' );
				$loader = new AssetLoader( $paths );
				$loader->register_core_assets();

				return $loader;
			}
		);
	}
}
