// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

using System.Reflection;
using GFramework.Core.Abstractions.Rule;
using GFramework.Core.Abstractions.Systems;

namespace GFramework.Core.Abstractions.Ioc;

/// <summary>
///     依赖注入容器接口，定义服务注册、解析与生命周期管理的统一入口。
/// </summary>
/// <remarks>
///     实现者必须在 <see cref="IDisposable.Dispose" /> 中释放容器拥有的根 <see cref="IServiceProvider" /> 及其
///     关联同步资源，并保证释放操作幂等。
///     容器一旦释放，后续任何注册、解析、查询或作用域创建调用都必须抛出
///     <see cref="ObjectDisposedException" />，避免消费者继续访问失效的运行时状态。
/// </remarks>
public interface IIocContainer : IContextAware, IDisposable
{
    #region Register Methods

    /// <summary>
    ///     注册单例
    ///     一个类型只允许一个实例
    /// </summary>
    /// <typeparam name="T">要注册为单例的类型</typeparam>
    /// <param name="instance">要注册的单例实例</param>
    /// <exception cref="InvalidOperationException">当该类型已经注册过单例时抛出异常</exception>
    void RegisterSingleton<T>(T instance);

    /// <summary>
    ///     注册单例服务，指定服务类型和实现类型
    ///     创建单例实例并在容器中注册
    /// </summary>
    /// <typeparam name="TService">服务接口或基类类型</typeparam>
    /// <typeparam name="TImpl">具体的实现类型</typeparam>
    void RegisterSingleton<TService, TImpl>()
        where TImpl : class, TService where TService : class;

    /// <summary>
    ///     注册瞬态服务，指定服务类型和实现类型
    ///     每次解析时都会创建新的实例
    /// </summary>
    /// <typeparam name="TService">服务接口或基类类型</typeparam>
    /// <typeparam name="TImpl">具体的实现类型</typeparam>
    void RegisterTransient<TService, TImpl>()
        where TImpl : class, TService where TService : class;

    /// <summary>
    ///     注册作用域服务，指定服务类型和实现类型
    ///     在同一作用域内共享实例，不同作用域使用不同实例
    /// </summary>
    /// <typeparam name="TService">服务接口或基类类型</typeparam>
    /// <typeparam name="TImpl">具体的实现类型</typeparam>
    void RegisterScoped<TService, TImpl>()
        where TImpl : class, TService where TService : class;

    /// <summary>
    ///     注册多个实例
    ///     将实例注册到其实现的所有接口和具体类型上
    /// </summary>
    /// <param name="instance">要注册的实例</param>
    public void RegisterPlurality(object instance);

    /// <summary>
    ///     注册多个实例
    ///     将实例注册到其实现所有接口
    /// </summary>
    /// <typeparam name="T">要注册的实例类型</typeparam>
    public void RegisterPlurality<T>() where T : class;

    /// <summary>
    ///     注册系统实例，将其绑定到其所有实现的接口上
    /// </summary>
    /// <param name="system">系统实例对象</param>
    void RegisterSystem(ISystem system);

    /// <summary>
    ///     注册指定类型的实例到容器中
    /// </summary>
    /// <typeparam name="T">要注册的实例类型</typeparam>
    /// <param name="instance">要注册的实例对象，不能为null</param>
    void Register<T>(T instance);

    /// <summary>
    ///     注册指定类型的实例到容器中
    /// </summary>
    /// <param name="type">要注册的实例类型</param>
    /// <param name="instance">要注册的实例对象</param>
    void Register(Type type, object instance);

    /// <summary>
    ///     注册工厂方法来创建服务实例
    ///     通过委托函数动态创建服务实例
    /// </summary>
    /// <typeparam name="TService">服务类型</typeparam>
    /// <param name="factory">创建服务实例的工厂委托函数</param>
    void RegisterFactory<TService>(Func<IServiceProvider, TService> factory) where TService : class;

    /// <summary>
    ///     注册 CQRS 请求管道行为。
    /// </summary>
    /// <typeparam name="TBehavior">行为类型，必须是引用类型</typeparam>
    void RegisterCqrsPipelineBehavior<TBehavior>()
        where TBehavior : class;

    /// <summary>
    ///     注册 CQRS 流式请求管道行为。
    /// </summary>
    /// <typeparam name="TBehavior">行为类型，必须是引用类型</typeparam>
    /// <exception cref="InvalidOperationException">容器已冻结，无法继续注册流式管道行为。</exception>
    /// <exception cref="ObjectDisposedException">容器已释放，无法继续注册流式管道行为。</exception>
    /// <remarks>
    ///     该入口既支持实现 <c>IStreamPipelineBehavior&lt;,&gt;</c> 的开放泛型行为类型，
    ///     也支持绑定到单一流式请求/响应对的封闭行为类型。
    ///     应在容器冻结前的注册阶段调用；具体可注册形态由实现容器负责校验。
    /// </remarks>
    void RegisterCqrsStreamPipelineBehavior<TBehavior>()
        where TBehavior : class;

    /// <summary>
    ///     从指定程序集显式注册 CQRS 处理器。
    ///     该入口适用于处理器不位于默认架构程序集中的场景，例如扩展包、模块程序集或拆分后的业务程序集。
    ///     运行时会优先使用程序集级源码生成注册器；若不存在可用注册器，则自动回退到反射扫描。
    /// </summary>
    /// <param name="assembly">包含 CQRS 处理器或生成注册器的程序集。</param>
    /// <exception cref="ArgumentNullException"><paramref name="assembly" /> 为 <see langword="null" />。</exception>
    /// <exception cref="InvalidOperationException">容器已冻结，无法继续注册 CQRS 处理器。</exception>
    void RegisterCqrsHandlersFromAssembly(Assembly assembly);

    /// <summary>
    ///     从多个程序集显式注册 CQRS 处理器。
    ///     容器会按稳定程序集键去重，避免默认启动路径与扩展模块重复接入同一程序集时产生重复 handler 映射。
    /// </summary>
    /// <param name="assemblies">要接入的程序集集合。</param>
    /// <exception cref="ArgumentNullException"><paramref name="assemblies" /> 为 <see langword="null" />。</exception>
    /// <exception cref="InvalidOperationException">容器已冻结，无法继续注册 CQRS 处理器。</exception>
    void RegisterCqrsHandlersFromAssemblies(IEnumerable<Assembly> assemblies);


    /// <summary>
    ///     配置服务
    /// </summary>
    /// <param name="configurator">服务配置委托</param>
    void ExecuteServicesHook(Action<IServiceCollection>? configurator = null);

    #endregion

    #region Get Methods

    /// <summary>
    ///     获取单个实例（通常用于具体类型）
    ///     如果存在多个，只返回第一个
    /// </summary>
    /// <typeparam name="T">期望获取的实例类型</typeparam>
    /// <returns>找到的第一个实例；如果未找到则返回 null</returns>
    /// <remarks>
    ///     在 <see cref="Freeze" /> 之前，该查询只保证返回已经物化为实例绑定的服务。
    ///     仅通过工厂或实现类型注册的服务在预冻结阶段可能不可见；若需要完整激活语义，请先冻结容器。
    /// </remarks>
    T? Get<T>() where T : class;

    /// <summary>
    ///     根据指定类型获取单个实例
    ///     如果存在多个，只返回第一个
    /// </summary>
    /// <param name="type">期望获取的实例类型</param>
    /// <returns>找到的第一个实例；如果未找到则返回 null</returns>
    /// <remarks>
    ///     在 <see cref="Freeze" /> 之前，该查询只保证返回已经物化为实例绑定的服务。
    ///     仅通过工厂或实现类型注册的服务在预冻结阶段可能不可见；若需要完整激活语义，请先冻结容器。
    /// </remarks>
    object? Get(Type type);


    /// <summary>
    ///     获取指定类型的必需实例
    /// </summary>
    /// <typeparam name="T">期望获取的实例类型</typeparam>
    /// <returns>找到的唯一实例</returns>
    /// <exception cref="InvalidOperationException">当没有注册实例或注册了多个实例时抛出</exception>
    T GetRequired<T>() where T : class;

    /// <summary>
    ///     获取指定类型的必需实例
    /// </summary>
    /// <param name="type">期望获取的实例类型</param>
    /// <returns>找到的唯一实例</returns>
    /// <exception cref="InvalidOperationException">当没有注册实例或注册了多个实例时抛出</exception>
    object GetRequired(Type type);


    /// <summary>
    ///     获取指定类型的所有实例（接口 / 抽象类推荐使用）
    /// </summary>
    /// <typeparam name="T">期望获取的实例类型</typeparam>
    /// <returns>所有符合条件的实例列表；如果没有则返回空数组</returns>
    /// <remarks>
    ///     在 <see cref="Freeze" /> 之前，该查询只会枚举当前已经可见的实例绑定，不会主动执行工厂或创建实现类型。
    /// </remarks>
    IReadOnlyList<T> GetAll<T>() where T : class;

    /// <summary>
    ///     获取指定类型的所有实例
    /// </summary>
    /// <param name="type">期望获取的实例类型</param>
    /// <returns>所有符合条件的实例列表；如果没有则返回空数组</returns>
    /// <remarks>
    ///     在 <see cref="Freeze" /> 之前，该查询只会枚举当前已经可见的实例绑定，不会主动执行工厂或创建实现类型。
    /// </remarks>
    IReadOnlyList<object> GetAll(Type type);


    /// <summary>
    ///     获取并排序（系统调度专用）
    /// </summary>
    /// <typeparam name="T">期望获取的实例类型</typeparam>
    /// <param name="comparison">比较器委托，定义排序规则</param>
    /// <returns>按指定方式排序后的实例列表</returns>
    IReadOnlyList<T> GetAllSorted<T>(Comparison<T> comparison) where T : class;

    /// <summary>
    ///     获取指定类型的所有实例，并按优先级排序
    ///     实现 IPrioritized 接口的服务将按优先级排序（数值越小优先级越高）
    ///     未实现 IPrioritized 的服务将使用默认优先级 0
    /// </summary>
    /// <typeparam name="T">期望获取的实例类型</typeparam>
    /// <returns>按优先级排序后的实例列表</returns>
    IReadOnlyList<T> GetAllByPriority<T>() where T : class;

    /// <summary>
    ///     获取指定类型的所有实例，并按优先级排序
    ///     实现 IPrioritized 接口的服务将按优先级排序（数值越小优先级越高）
    ///     未实现 IPrioritized 的服务将使用默认优先级 0
    /// </summary>
    /// <param name="type">期望获取的实例类型</param>
    /// <returns>按优先级排序后的实例列表</returns>
    IReadOnlyList<object> GetAllByPriority(Type type);

    #endregion

    #region Utility Methods

    /// <summary>
    ///     检查容器中是否包含指定类型的实例
    /// </summary>
    /// <typeparam name="T">要检查的类型</typeparam>
    /// <returns>如果容器中包含指定类型的实例则返回true，否则返回false</returns>
    /// <remarks>
    ///     在 <see cref="Freeze" /> 之前，该方法更接近“是否存在对应注册”的检查，而不是完整的 DI 可解析性判断。
    /// </remarks>
    bool Contains<T>() where T : class;

    /// <summary>
    ///     检查容器中是否存在可赋值给指定服务类型的注册项，而不要求解析出实例。
    /// </summary>
    /// <param name="type">要检查的服务类型。</param>
    /// <returns>若存在显式注册或开放泛型映射可满足该服务类型，则返回 <see langword="true" />；否则返回 <see langword="false" />。</returns>
    /// <exception cref="ArgumentNullException">当 <paramref name="type" /> 为 <see langword="null" /> 时抛出。</exception>
    /// <exception cref="ObjectDisposedException">当调用 <see cref="HasRegistration(Type)" /> 时容器已被释放时抛出。</exception>
    /// <remarks>
    ///     该入口面向“先判断是否值得解析实例”的热路径优化场景。
    ///     与 <see cref="Contains{T}" /> 不同，它不会为了判断结果而激活服务实例，因此可避免把瞬态对象创建、
    ///     多服务枚举或日志分配混入仅需存在性判断的调用链中。
    ///     该方法按服务键与开放泛型映射判断可见性，不会把“仅以实现类型自身注册”的实例误判成其所有可赋值接口都已注册。
    /// </remarks>
    bool HasRegistration(Type type);

    /// <summary>
    ///     判断容器中是否包含某个具体的实例对象
    /// </summary>
    /// <param name="instance">待查询的实例对象</param>
    /// <returns>若容器中包含该实例则返回true，否则返回false</returns>
    bool ContainsInstance(object instance);

    /// <summary>
    ///     清空容器中的所有实例
    /// </summary>
    void Clear();

    /// <summary>
    ///     冻结容器，防止后续修改
    ///     调用此方法后，容器将变为只读状态，不能再注册新的服务实例
    /// </summary>
    void Freeze();

    /// <summary>
    ///     获取底层的服务集合
    ///     提供对内部IServiceCollection的访问权限，用于高级配置和自定义操作
    /// </summary>
    /// <returns>底层的IServiceCollection实例</returns>
    IServiceCollection GetServicesUnsafe { get; }

    /// <summary>
    ///     创建一个新的服务作用域
    ///     作用域内的 Scoped 服务将共享同一实例
    /// </summary>
    /// <returns>服务作用域实例</returns>
    IServiceScope CreateScope();

    #endregion
}
