using System.ComponentModel;

namespace ThingsGateway.NewLife.Model;

/// <summary>对象容器助手。扩展方法专用</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static class ObjectContainerHelper
{
    #region 单实例注册
    /// <summary>添加单实例，指定实现类型</summary>
    /// <param name="container"></param>
    /// <param name="serviceType"></param>
    /// <param name="implementationType"></param>
    /// <returns></returns>
    public static IObjectContainer AddSingleton(this IObjectContainer container, Type serviceType, Type implementationType)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));
        if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
        if (implementationType == null) throw new ArgumentNullException(nameof(implementationType));

        var item = new ServiceDescriptor(serviceType, implementationType)
        {
            Lifetime = ObjectLifetime.Singleton,
        };
        container.Add(item);

        return container;
    }

    /// <summary>添加单实例，指定实现类型</summary>
    /// <typeparam name="TService"></typeparam>
    /// <typeparam name="TImplementation"></typeparam>
    /// <param name="container"></param>
    /// <returns></returns>
    public static IObjectContainer AddSingleton<TService, TImplementation>(this IObjectContainer container) where TService : class where TImplementation : class, TService => container.AddSingleton(typeof(TService), typeof(TImplementation));

    /// <summary>添加单实例，指定实例工厂</summary>
    /// <param name="container"></param>
    /// <param name="serviceType"></param>
    /// <param name="factory"></param>
    /// <returns></returns>
    public static IObjectContainer AddSingleton(this IObjectContainer container, Type serviceType, Func<IServiceProvider, Object> factory)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));
        if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
        if (factory == null) throw new ArgumentNullException(nameof(factory));

        var item = new ServiceDescriptor(serviceType)
        {
            Factory = factory,
            Lifetime = ObjectLifetime.Singleton,
        };
        container.Add(item);

        return container;
    }

    /// <summary>添加单实例，指定实例工厂</summary>
    /// <typeparam name="TService"></typeparam>
    /// <param name="container"></param>
    /// <param name="factory"></param>
    /// <returns></returns>
    public static IObjectContainer AddSingleton<TService>(this IObjectContainer container, Func<IServiceProvider, TService> factory) where TService : class => container.AddSingleton(typeof(TService), factory);

    /// <summary>添加单实例，指定实例</summary>
    /// <param name="container"></param>
    /// <param name="serviceType"></param>
    /// <param name="instance"></param>
    /// <returns></returns>
    public static IObjectContainer AddSingleton(this IObjectContainer container, Type serviceType, Object? instance)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));
        if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
        //if (instance == null) throw new ArgumentNullException(nameof(instance));
        // 内部可能直接实例化，不需要实例
        if (instance == null && (serviceType.IsAbstract || serviceType.IsInterface)) throw new ArgumentNullException(nameof(instance));

        var item = new ServiceDescriptor(serviceType)
        {
            Instance = instance,
            Lifetime = ObjectLifetime.Singleton,
        };
        container.Add(item);

        return container;
    }

    /// <summary>添加单实例，指定实例</summary>
    /// <typeparam name="TService"></typeparam>
    /// <param name="container"></param>
    /// <param name="instance"></param>
    /// <returns></returns>
    public static IObjectContainer AddSingleton<TService>(this IObjectContainer container, TService? instance = null) where TService : class => container.AddSingleton(typeof(TService), instance);

    /// <summary>尝试添加单实例，指定实现类型</summary>
    /// <param name="container"></param>
    /// <param name="serviceType"></param>
    /// <param name="implementationType"></param>
    /// <returns></returns>
    public static IObjectContainer TryAddSingleton(this IObjectContainer container, Type serviceType, Type implementationType)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));
        if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
        if (implementationType == null) throw new ArgumentNullException(nameof(implementationType));

        var item = new ServiceDescriptor(serviceType, implementationType)
        {
            Lifetime = ObjectLifetime.Singleton,
        };
        container.TryAdd(item);

        return container;
    }

    /// <summary>尝试添加单实例，指定实现类型</summary>
    /// <typeparam name="TService"></typeparam>
    /// <typeparam name="TImplementation"></typeparam>
    /// <param name="container"></param>
    /// <returns></returns>
    public static IObjectContainer TryAddSingleton<TService, TImplementation>(this IObjectContainer container) where TService : class where TImplementation : class, TService => container.TryAddSingleton(typeof(TService), typeof(TImplementation));

    /// <summary>尝试添加单实例，指定实例</summary>
    /// <typeparam name="TService"></typeparam>
    /// <param name="container"></param>
    /// <param name="instance"></param>
    /// <returns></returns>
    public static IObjectContainer TryAddSingleton<TService>(this IObjectContainer container, TService? instance = null) where TService : class
    {
        if (container == null) throw new ArgumentNullException(nameof(container));

        var item = new ServiceDescriptor(typeof(TService))
        {
            Instance = instance,
            Lifetime = ObjectLifetime.Singleton,
        };
        if (instance == null) item.ImplementationType = typeof(TService);
        container.TryAdd(item);

        return container;
    }
    #endregion

    #region 范围容器
    /// <summary>添加范围容器实例，指定实现类型</summary>
    /// <param name="container"></param>
    /// <param name="serviceType"></param>
    /// <param name="implementationType"></param>
    /// <returns></returns>
    public static IObjectContainer AddScoped(this IObjectContainer container, Type serviceType, Type implementationType)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));
        if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
        if (implementationType == null) throw new ArgumentNullException(nameof(implementationType));

        var item = new ServiceDescriptor(serviceType, implementationType)
        {
            Lifetime = ObjectLifetime.Scoped,
        };
        container.Add(item);

        return container;
    }

    /// <summary>添加范围容器实例，指定实现类型</summary>
    /// <typeparam name="TService"></typeparam>
    /// <typeparam name="TImplementation"></typeparam>
    /// <param name="container"></param>
    /// <returns></returns>
    public static IObjectContainer AddScoped<TService, TImplementation>(this IObjectContainer container) where TService : class where TImplementation : class, TService => container.AddScoped(typeof(TService), typeof(TImplementation));

    /// <summary>添加范围容器实例，指定实现类型</summary>
    /// <typeparam name="TService"></typeparam>
    /// <param name="container"></param>
    /// <returns></returns>
    public static IObjectContainer AddScoped<TService>(this IObjectContainer container) where TService : class => container.AddScoped(typeof(TService), typeof(TService));

    /// <summary>添加范围容器实例，指定实现工厂</summary>
    /// <param name="container"></param>
    /// <param name="serviceType"></param>
    /// <param name="factory"></param>
    /// <returns></returns>
    public static IObjectContainer AddScoped(this IObjectContainer container, Type serviceType, Func<IServiceProvider, Object> factory)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));
        if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
        if (factory == null) throw new ArgumentNullException(nameof(factory));

        var item = new ServiceDescriptor(serviceType)
        {
            Factory = factory,
            Lifetime = ObjectLifetime.Scoped,
        };
        container.Add(item);

        return container;
    }

    /// <summary>添加范围容器实例，指定实现工厂</summary>
    /// <typeparam name="TService"></typeparam>
    /// <param name="container"></param>
    /// <param name="factory"></param>
    /// <returns></returns>
    public static IObjectContainer AddScoped<TService>(this IObjectContainer container, Func<IServiceProvider, Object> factory) where TService : class => container.AddScoped(typeof(TService), factory);

    /// <summary>添加范围容器实例，指定实现类型</summary>
    /// <param name="container"></param>
    /// <param name="serviceType"></param>
    /// <param name="implementationType"></param>
    /// <returns></returns>
    public static IObjectContainer TryAddScoped(this IObjectContainer container, Type serviceType, Type implementationType)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));
        if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
        if (implementationType == null) throw new ArgumentNullException(nameof(implementationType));

        var item = new ServiceDescriptor(serviceType, implementationType)
        {
            Lifetime = ObjectLifetime.Scoped,
        };
        container.TryAdd(item);

        return container;
    }

    /// <summary>尝试添加范围容器实例，指定实现类型</summary>
    /// <typeparam name="TService"></typeparam>
    /// <typeparam name="TImplementation"></typeparam>
    /// <param name="container"></param>
    /// <returns></returns>
    public static IObjectContainer TryAddScoped<TService, TImplementation>(this IObjectContainer container) where TService : class where TImplementation : class, TService => container.TryAddScoped(typeof(TService), typeof(TImplementation));

    /// <summary>尝试添加范围容器实例，指定实例</summary>
    /// <typeparam name="TService"></typeparam>
    /// <param name="container"></param>
    /// <param name="instance"></param>
    /// <returns></returns>
    public static IObjectContainer TryAddScoped<TService>(this IObjectContainer container, TService? instance = null) where TService : class
    {
        if (container == null) throw new ArgumentNullException(nameof(container));

        var item = new ServiceDescriptor(typeof(TService))
        {
            Instance = instance,
            Lifetime = ObjectLifetime.Scoped,
        };
        if (instance == null) item.ImplementationType = typeof(TService);
        container.TryAdd(item);

        return container;
    }
    #endregion

    #region 瞬态注册
    /// <summary>添加瞬态实例，指定实现类型</summary>
    /// <param name="container"></param>
    /// <param name="serviceType"></param>
    /// <param name="implementationType"></param>
    /// <returns></returns>
    public static IObjectContainer AddTransient(this IObjectContainer container, Type serviceType, Type implementationType)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));
        if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
        if (implementationType == null) throw new ArgumentNullException(nameof(implementationType));

        var item = new ServiceDescriptor(serviceType, implementationType)
        {
            Lifetime = ObjectLifetime.Transient,
        };
        container.Add(item);

        return container;
    }

    /// <summary>添加瞬态实例，指定实现类型</summary>
    /// <typeparam name="TService"></typeparam>
    /// <typeparam name="TImplementation"></typeparam>
    /// <param name="container"></param>
    /// <returns></returns>
    public static IObjectContainer AddTransient<TService, TImplementation>(this IObjectContainer container) where TService : class where TImplementation : class, TService => container.AddTransient(typeof(TService), typeof(TImplementation));

    /// <summary>添加瞬态实例，指定实现类型</summary>
    /// <typeparam name="TService"></typeparam>
    /// <param name="container"></param>
    /// <returns></returns>
    public static IObjectContainer AddTransient<TService>(this IObjectContainer container) where TService : class => container.AddTransient(typeof(TService), typeof(TService));

    /// <summary>添加瞬态实例，指定实现工厂</summary>
    /// <param name="container"></param>
    /// <param name="serviceType"></param>
    /// <param name="factory"></param>
    /// <returns></returns>
    public static IObjectContainer AddTransient(this IObjectContainer container, Type serviceType, Func<IServiceProvider, Object> factory)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));
        if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
        if (factory == null) throw new ArgumentNullException(nameof(factory));

        var item = new ServiceDescriptor(serviceType)
        {
            Factory = factory,
            Lifetime = ObjectLifetime.Transient,
        };
        container.Add(item);

        return container;
    }

    /// <summary>添加瞬态实例，指定实现工厂</summary>
    /// <typeparam name="TService"></typeparam>
    /// <param name="container"></param>
    /// <param name="factory"></param>
    /// <returns></returns>
    public static IObjectContainer AddTransient<TService>(this IObjectContainer container, Func<IServiceProvider, Object> factory) where TService : class => container.AddTransient(typeof(TService), factory);

    /// <summary>添加瞬态实例，指定实现类型</summary>
    /// <param name="container"></param>
    /// <param name="serviceType"></param>
    /// <param name="implementationType"></param>
    /// <returns></returns>
    public static IObjectContainer TryAddTransient(this IObjectContainer container, Type serviceType, Type implementationType)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));
        if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
        if (implementationType == null) throw new ArgumentNullException(nameof(implementationType));

        var item = new ServiceDescriptor(serviceType, implementationType)
        {
            Lifetime = ObjectLifetime.Transient,
        };
        container.TryAdd(item);

        return container;
    }

    /// <summary>尝试添加瞬态实例，指定实现类型</summary>
    /// <typeparam name="TService"></typeparam>
    /// <typeparam name="TImplementation"></typeparam>
    /// <param name="container"></param>
    /// <returns></returns>
    public static IObjectContainer TryAddTransient<TService, TImplementation>(this IObjectContainer container) where TService : class where TImplementation : class, TService => container.TryAddTransient(typeof(TService), typeof(TImplementation));

    /// <summary>尝试添加瞬态实例，指定实例</summary>
    /// <typeparam name="TService"></typeparam>
    /// <param name="container"></param>
    /// <param name="instance"></param>
    /// <returns></returns>
    public static IObjectContainer TryAddTransient<TService>(this IObjectContainer container, TService? instance = null) where TService : class
    {
        if (container == null) throw new ArgumentNullException(nameof(container));

        var item = new ServiceDescriptor(typeof(TService))
        {
            Instance = instance,
            Lifetime = ObjectLifetime.Transient,
        };
        if (instance == null) item.ImplementationType = typeof(TService);
        container.TryAdd(item);

        return container;
    }
    #endregion

    #region 构建
    /// <summary>从对象容器创建服务提供者</summary>
    /// <param name="container">对象容器</param>
    /// <returns></returns>
    public static IServiceProvider BuildServiceProvider(this IObjectContainer container) => BuildServiceProvider(container, null);

    /// <summary>从对象容器创建服务提供者，支持传入另一个服务提供者作为内部提供者</summary>
    /// <param name="container">对象容器</param>
    /// <param name="innerServiceProvider">内部服务提供者，常用于NETCore的IoC</param>
    /// <returns></returns>
    public static IServiceProvider BuildServiceProvider(this IObjectContainer container, IServiceProvider? innerServiceProvider)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));

        var provider = container.Resolve(typeof(IServiceProvider)) as IServiceProvider;
        if (provider != null) return provider;

        var provider2 = new ServiceProvider(container, innerServiceProvider);

        container.TryAddSingleton(provider2);

        provider = container.Resolve(typeof(IServiceProvider)) as IServiceProvider;
        if (provider != null) return provider;

        return provider2;
    }

    /// <summary>从对象容器创建应用主机</summary>
    /// <param name="container"></param>
    /// <returns></returns>
    public static IHost BuildHost(this IObjectContainer container)
    {
        // 尝试注册应用主机，如果前面已经注册，则这里无效
        container.TryAddSingleton<IHost, Host>();

        //return new Host(container.BuildServiceProvider());

        var provider = container.BuildServiceProvider();
        return provider.GetRequiredService<IHost>();
    }
    #endregion

    #region 旧版方法
    /// <summary>解析类型的实例</summary>
    /// <typeparam name="TService">接口类型</typeparam>
    /// <param name="container">对象容器</param>
    /// <returns></returns>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public static TService? Resolve<TService>(this IObjectContainer container) => (TService?)container.Resolve(typeof(TService));
    #endregion
}