using CloudBuilder.Core.Application.Options;
using CloudBuilder.Core.Application.Packs;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;

namespace CloudBuilder.Core.Application.Builders
{
    /// <summary>
    /// CloudBuilder构建器
    /// </summary>
    public class CloudBuilderBuilder
    {
        public CloudBuilderBuilder(IServiceCollection services, IConfiguration configuration)
        {
            this.Services = services;
            AddOptions(configuration);
        }

        /// <summary>
        /// 获取 服务集合
        /// </summary>
        public IServiceCollection Services { get; }

        /// <summary>
        /// 获取 CloudBuilder选项配置委托
        /// </summary>
        public CloudBuilderOptions CloudBuilderOptions { get; private set; }

        /// <summary>
        /// 添加所有模块
        /// </summary>
        /// <typeparam name="TPack">要添加的模块类型</typeparam>
        public CloudBuilderBuilder AddPack()
        {
            var baseType = typeof(CloudBuilderPack);

            var allAssemblies = GetAssemblies();

            var types = allAssemblies
                .SelectMany(a => a.DefinedTypes)
                .Select(type => type.AsType())
                .Where(type => type != baseType && baseType.IsAssignableFrom(type))
                .Where(x => x.IsClass)
                .OrderBy(x => x.GetCustomAttribute<PackLevelAttribute>()?.Level).ToList();

            foreach (var implementType in types)
            {
                if (Services.Any(m => m.ServiceType == baseType && m.ImplementationType == implementType))
                {
                    continue;
                }

                //注入CloudBuilderPack模块内的服务
                CloudBuilderPack pack = (CloudBuilderPack)Activator.CreateInstance(
                   implementType,
                   BindingFlags.Public | BindingFlags.Instance,
                   null,
                   null,
                   null);

                pack.AddServices(Services, this);
            }

            return this;
        }

        /// <summary>
        /// 添加CloudBuilder选项配置
        /// </summary>
        /// <param name="optionsAction">CloudBuilder操作选项</param>
        /// <returns>CloudBuilder构建器</returns>
        public CloudBuilderBuilder AddOptions(IConfiguration configuration)
        {
            CloudBuilderOptions options = new CloudBuilderOptions();
            new CloudBuilderOptionsSetup(configuration).Configure(options);

            CloudBuilderOptions = options;
            return this;
        }

        public T GetTransientService<T>()
        {
            object? t = Services.BuildServiceProvider().GetService(typeof(T));
            if (t == null) return default(T)!;
            return (T)t;
        }

        public T GetTransientRequiredService<T>()
        {
            return (T)Services.BuildServiceProvider().GetRequiredService(typeof(T));
        }

        public static Assembly[] GetAssemblies()
        {
            // 获取入口程序集
            var entryAssembly = Assembly.GetEntryAssembly();
            List<string> reference = new List<string>();

            if (!string.IsNullOrWhiteSpace(entryAssembly.Location))
            {
                var dependencyContext = DependencyContext.Default;

                // 读取项目程序集或 Furion 官方发布的包，或手动添加引用的dll，或配置特定的包前缀
                reference = dependencyContext.RuntimeLibraries
                    .Where(u => u.Type == "project" || u.Type == "reference")
                    .Select(x => x.Name).ToList();
            }

            string[] filters =
            {
                "mscorlib",
                "netstandard",
                "dotnet",
                "api-ms-win-core",
                "runtime.",
                "System",
                "Microsoft",
                "Window",
            };

            //遍历文件夹的方式，用于传统.netfx
            string path = AppContext.BaseDirectory;
            string[] files = Directory.GetFiles(path, "*.dll", SearchOption.TopDirectoryOnly)
                .Where(x => reference.Contains(Path.GetFileNameWithoutExtension(x))).ToArray();

            return files.Where(file => filters.All(token => Path.GetFileName(file)?.StartsWith(token) != true)).Select(Assembly.LoadFrom).ToArray();
        }


    }
}
