<?php
declare( strict_types=1 );

namespace FiboFilters\DI;

use Exception;
use FiboFiltersVendor\Psr\Container\ContainerInterface;
use FiboFiltersVendor\Psr\Container\NotFoundExceptionInterface;
use FiboFiltersVendor\Symfony\Component\DependencyInjection\Container;
use FiboFiltersVendor\Thanks_To_IT\WP_DICH\WP_DICH;

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

/**
 * Class ContainerWrapper
 */
class ContainerWrapper implements ContainerInterface {

	/** @var Container */
	private $free_container;

	/** @var Container|null */
	private $pro_container;

	/** @var ContainerWrapper|null */
	private static $instance;

	public function __construct( Container $free_container, Container $pro_container = null ) {
		$this->free_container = $free_container;
		$this->pro_container  = $pro_container;
	}

	/**
	 * @param string $id Identifier of the entry to get.
	 *
	 * @return mixed
	 * @throws Exception Exception.
	 */ // phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.Missing
	public function get( $id ) { // phpcs:ignore
		try {
			return $this->free_container->get( $id );
		} catch ( NotFoundExceptionInterface $e ) {
			if ( ! $this->pro_container ) {
				throw $e;
			}

			return $this->pro_container->get( $id );
		}
	}

	/**
	 * @param string $id Identifier of the entry to look for.
	 */
	public function has( $id ): bool { // phpcs:ignore
		return $this->free_container->has( $id ) || ( $this->pro_container && $this->pro_container->has( $id ) );
	}

	/**
	 * @return ContainerInterface|null
	 */
	public function get_pro_container() {
		if ( ! $this->pro_container && class_exists( ContainerConfiguratorPro::class ) ) {
			$this->pro_container = self::create_pro_container( $this->free_container );
		}

		return $this->pro_container;
	}

	/**
	 * @return ContainerWrapper
	 */
	public static function get_instance(): ContainerWrapper {
		if ( self::$instance ) {
			return self::$instance;
		}
		$free_container_factory = new ContainerFactory( new ContainerConfigurator() );
		$free_container         = $free_container_factory->get_container();
		$pro_container          = null;
		if ( class_exists( ContainerConfiguratorPro::class ) ) {
			$pro_container = self::create_pro_container( $free_container );
		}
		self::$instance = new ContainerWrapper( $free_container, $pro_container );

		// Init dynamic service (see: https://github.com/thanks-to-it/wp-dich).
		$free_container->set( 'dich', new WP_DICH( self::$instance ) );

		return self::$instance;
	}

	/**
	 * Get WP_DICH service
	 *
	 * @return object|null|WP_DICH
	 *
	 * @throws Exception Exception.
	 */
	public function dich() {
		return $this->get( 'dich' );
	}

	/**
	 * @param Container $free_container Free container.
	 */
	private static function create_pro_container( Container $free_container ): Container {
		$pro_container_factory = new ContainerFactory( new ContainerConfiguratorPro() );
		$pro_container         = $pro_container_factory->get_container();
		$pro_container->set( IContainerConfigurator::FREE_CONTAINER_SERVICE_SLUG, $free_container );
		$free_container->set( IContainerConfigurator::PRO_CONTAINER_SERVICE_SLUG, $pro_container );

		return $pro_container;
	}
}
