<?php
/**
 * Dependency injection container.
 *
 * @package Carticy\Conversions\Core
 */

declare(strict_types=1);

namespace Carticy\Conversions\Core;

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

/**
 * Minimal lazy service container.
 */
final class Container {

	/**
	 * Service factories.
	 *
	 * @var array<string, callable>
	 */
	private array $factories = array();

	/**
	 * Cached instances.
	 *
	 * @var array<string, object>
	 */
	private array $instances = array();

	/**
	 * Register a service factory.
	 *
	 * @param string   $class_name Fully qualified class name.
	 * @param callable $factory Factory callback.
	 */
	public function register( string $class_name, callable $factory ): void {
		$this->factories[ $class_name ] = $factory;
	}

	/**
	 * Get a service instance.
	 *
	 * @template T of object
	 * @param string $class_name Fully qualified class name.
	 * @phpstan-param class-string<T> $class_name
	 * @return T
	 * @throws \InvalidArgumentException When the service is not registered.
	 */
	public function get( string $class_name ): object {
		if ( isset( $this->instances[ $class_name ] ) ) {
			return $this->instances[ $class_name ];
		}

		if ( ! isset( $this->factories[ $class_name ] ) ) {
			throw new \InvalidArgumentException(
				'Requested service is not registered in the container.'
			);
		}

		$this->instances[ $class_name ] = ( $this->factories[ $class_name ] )();

		return $this->instances[ $class_name ];
	}

	/**
	 * Check whether a service is registered.
	 *
	 * @param string $class_name Fully qualified class name.
	 * @return bool
	 */
	public function has( string $class_name ): bool {
		return isset( $this->factories[ $class_name ] );
	}
}
