<?php
/**
 * Dependency Injection Container
 *
 * Simple DI container for managing service instances.
 *
 * @package DonorCore\Utils
 */

namespace DonorCore\Utils;

defined( 'ABSPATH' ) || exit;

/**
 * DI Container Class
 */
class Container {

	/**
	 * Registered services.
	 *
	 * @var array
	 */
	private $services = array();

	/**
	 * Resolved service instances.
	 *
	 * @var array
	 */
	private $instances = array();

	/**
	 * Register a service.
	 *
	 * @param string          $name     Service identifier.
	 * @param string|callable $concrete Class name or factory callable.
	 * @param callable|null   $factory  Optional factory function.
	 */
	public function register( $name, $concrete, $factory = null ) {
		$this->services[ $name ] = array(
			'concrete' => $concrete,
			'factory'  => $factory,
		);
	}

	/**
	 * Get a service instance.
	 *
	 * @param string $name Service identifier.
	 * @return mixed Service instance.
	 * @throws \Exception If service not registered.
	 */
	public function get( $name ) {
		// Return cached instance if already resolved.
		if ( isset( $this->instances[ $name ] ) ) {
			return $this->instances[ $name ];
		}

		// Check if service is registered.
		if ( ! isset( $this->services[ $name ] ) ) {
			throw new \Exception( sprintf( 'Service "%s" is not registered.', esc_html( $name ) ) );
		}

		$service = $this->services[ $name ];

		// Use factory if provided.
		if ( isset( $service['factory'] ) && is_callable( $service['factory'] ) ) {
			$instance = call_user_func( $service['factory'] );
		} elseif ( is_string( $service['concrete'] ) && class_exists( $service['concrete'] ) ) {
			// Instantiate class.
			$instance = new $service['concrete']();
		} elseif ( is_callable( $service['concrete'] ) ) {
			// Call factory.
			$instance = call_user_func( $service['concrete'] );
		} else {
			throw new \Exception( sprintf( 'Unable to resolve service "%s".', esc_html( $name ) ) );
		}

		// Cache instance.
		$this->instances[ $name ] = $instance;

		return $instance;
	}

	/**
	 * Check if a service is registered.
	 *
	 * @param string $name Service identifier.
	 * @return bool
	 */
	public function has( $name ) {
		return isset( $this->services[ $name ] );
	}
}
