<?php

namespace xmvc;

/**
 * 容器
 */
class Container
{
    /** 容器自己的唯一实例 */
    private static ?self $selfInstance = null;
    private array $services = [];
    private array $instances = [];

    private function __construct()
    {
        $cfg = loadConfig('service');
        foreach ($cfg as $name => $definition) {
            $this->services[$name] = $definition;
        }
    }

    /** 获取容器自己的维一实例 */
    public static function getInstance(): self
    {
        if (null === static::$selfInstance) {
            static::$selfInstance = new static();
        }
        return static::$selfInstance;
    }

    /** 服务实例是否存在 */
    public static function has(string $name): bool
    {
        return static::getInstance()->callHas($name);
    }

    /** 设置服务实例 */
    public static function set(string $name, callable $definition): void
    {
        static::getInstance()->callSet($name, $definition);
    }

    /** 获取服务实例 */
    public static function get(string $name): object|array
    {
        return static::getInstance()->callGet($name);
    }

    /** 延时创建 service.php 中的配置为 [类名, null|[创建参数1, 创建参数2...]] */
    public static function lazyCreate(string $name): object
    {
        return static::getInstance()->callLazyCreate($name);
    }

    /** 按前缀获取服务实例 */
    public static function getByPrefix(string $prefix)
    {
        $container = static::getInstance();
        $matchedServices = [];
        foreach ($container->services as $name => $definition) {
            if (strpos($name, $prefix) === 0) {
                $matchedServices[$name] = $container->callGet($name);
            }
        }
        return $matchedServices;
    }

    private function callHas(string $name): bool
    {
        return isset($this->instances[$name]);
    }

    private function callSet(string $name, callable $definition): void
    {
        $this->services[$name] = $definition;
    }

    private function callGet(string $name): object|array
    {
        if (isset($this->instances[$name])) {
            return $this->instances[$name];
        }

        if (!isset($this->services[$name])) {
            error("服务在容器中没有找到: " . $name, null, [[__FILE__,__LINE__]]);
        }

        $definition = $this->services[$name];
        $service = $definition($this);
        if (is_object($service)) {
            $this->instances[$name] = $service;
        }
        return $service;
    }

    private function callLazyCreate(string $name): object
    {
        if (isset($this->instances[$name])) {
            return $this->instances[$name];
        }
        $definition = $this->services[$name];
        $service = $definition($this);
        if (is_array($service)) {
            // [类名, null|[创建参数1, 创建参数2...]] 延时加载模式
            $service[1] = $service[1] ?: [];
            $service = new $service[0](...$service[1]);
        }
        return $service;
    }
}
