using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace XF.Domain.Dependency;

public static class DependencyInjectionExtensions
{
    public static IServiceCollection BatchInjectServices(this IServiceCollection services, Func<Type, bool>? predicate = null)
    {
        var assemblies = GetAllAssembly();

        predicate ??= d => true;
        InjectConventionServices(services, assemblies, predicate);
        InjectBackgroundServices(services, assemblies, predicate);

        return services;
    }

    private static void InjectBackgroundServices(IServiceCollection services, List<Assembly> assemblies, Func<Type, bool> predicate)
    {
        var types = assemblies.SelectMany(t => t.GetTypes())
            .Where(t => typeof(BackgroundService).IsAssignableFrom(t) &&
                        t.IsClass && !t.IsAbstract && predicate(t) &&
                        t.GetInterfaces().All(d => d != typeof(IIgnoreDependency))).ToList();

        foreach (var type in types)
        {
            services.AddSingleton(typeof(IHostedService), type);
        }
    }
    private static void InjectConventionServices(IServiceCollection services, List<Assembly> assemblies, Func<Type, bool> predicate)
    {
        assemblies.SelectMany(d => d.GetTypes())
            .Where(d => d.GetInterfaces().Any(x =>
                x == typeof(IScopeDependency)
                || x == typeof(ISingletonDependency)
                || x == typeof(ITransientDependency))
                        && d.GetInterfaces().All(x => x != typeof(IIgnoreDependency))
                        && predicate(d))
            .ToList()
            .ForEach(d => ServiceRegister.Register(services, d));
    }

    /// <summary>
    /// 获取全部 Assembly
    /// </summary>
    /// <returns></returns>
    private static List<Assembly> GetAllAssembly()
    {
        var allAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();

        return allAssemblies;
        //
        //HashSet<string> loadedAssemblies = new();

        //foreach (var item in allAssemblies)
        //{
        //    loadedAssemblies.Add(item.FullName!);
        //}

        //Queue<Assembly> assembliesToCheck = new();
        //assembliesToCheck.Enqueue(Assembly.GetEntryAssembly()!);

        //while (assembliesToCheck.Any())
        //{
        //    var assemblyToCheck = assembliesToCheck.Dequeue();
        //    var referencedAssemblies = assemblyToCheck!.GetReferencedAssemblies();
        //    foreach (var reference in referencedAssemblies)
        //    {
        //        if (!loadedAssemblies.Contains(reference.FullName))
        //        {
        //            var assembly = Assembly.Load(reference);
        //            assembliesToCheck.Enqueue(assembly);

        //            loadedAssemblies.Add(reference.FullName);

        //            allAssemblies.Add(assembly);

        //        }
        //    }
        //}
        //return allAssemblies;
    }
}

public class ServiceRegister
{
    public static void Register(IServiceCollection services, Type type)
    {
        //if (type.Name == "IUserCacheManager")
        //    throw new Exception();
        if (type == null) return;
        var serviceType = GetServiceType(type);
        if (HasImplInterface(type, typeof(ISingletonDependency)))
        {
            services.AddSingleton(serviceType, type);
            //Console.WriteLine($"lifeTime: singleton, {serviceType.FullName} => {type.FullName}");
        }
        else if (HasImplInterface(type, typeof(IScopeDependency)))
        {
            services.AddScoped(serviceType, type);
            //Console.WriteLine($"lifeTime: scope, {serviceType.FullName} => {type.FullName}");
        }
        else if (HasImplInterface(type, typeof(ITransientDependency)))
        {
            services.AddTransient(serviceType, type);
            //Console.WriteLine($"lifeTime: transient, {serviceType.FullName} => {type.FullName}");
        }
        else
        {
            throw new ArgumentOutOfRangeException("不支持的注入方式");
        }
    }

    private static Type GetServiceType(Type type)
    {
        var isSelfInjection = type.GetInterfaces().Any(d => d == typeof(ISelfDependency));
        if (isSelfInjection) return type;
        var typeName = type.Name;
        var implTypeName = "I" + typeName;
        var nameModeType = type.GetInterfaces().FirstOrDefault(d => d.Name == implTypeName);
        if (nameModeType != null) return nameModeType;
        return type.GetInterfaces().FirstOrDefault() ?? type;
    }

    private static bool HasImplInterface(Type type, Type iType) =>
        type.GetInterfaces().Any(d => d == iType);
}