using MongoDB.Libmongocrypt;
using Sirenix.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;

namespace ET
{
    public static class BuildAssembliesHelper
    {
        public const string CodeDir = "./Assets/Res/CodeDll/";
        private const string BattleDllDir = "../../WSHCommon/Library/Debug/";
        private static List<string>RefBattleDlls = new List<string>();

        public static void BuildModel(CodeOptimization codeOptimization, GlobalConfig globalConfig)
        {
            try
            {
                string[] logicFiles = Directory.GetFiles(Define.BuildOutputDir, "Model*");
                foreach (string file in logicFiles)
                {
                    File.Delete(file);
                }
            }
            catch { }

            //int random = RandomGenerator.RandomNumber(100000000, 999999999);
            string logicFile = $"Model";

            List<string> codes;

            switch (globalConfig.CodeMode)
            {
                case CodeMode.Client:
                    codes = new List<string>()
                    {
                        "Assets/Scripts/Codes/Model/",
                        "Assets/Scripts/Codes/ModelView/Client/",
                    };
                    break;
                default:
                    throw new Exception("unknow 'CodeMode'");
            }

            BuildMuteAssembly(logicFile, codes, GetBatlleDlls().ToArray(), codeOptimization, globalConfig.CodeMode);

            File.Copy(Path.Combine(Define.BuildOutputDir, $"{logicFile}.dll"), Path.Combine(CodeDir, $"Model.dll.bytes"), true);
            File.Copy(Path.Combine(Define.BuildOutputDir, $"{logicFile}.pdb"), Path.Combine(CodeDir, $"Model.pdb.bytes"), true);
            Debug.Log("copy Model.dll to Assets/Res/CodeDll success!");
        }

        public static void BuildHotfix(CodeOptimization codeOptimization, GlobalConfig globalConfig)
        {
            try
            {
                string[] logicFiles = Directory.GetFiles(Define.BuildOutputDir, "Hotfix_*");
                foreach (string file in logicFiles)
                {
                    File.Delete(file);
                }
            }
            catch { }

            string modelfile;
            string[] modelFiles = Directory.GetFiles(Define.BuildOutputDir, "Model*.dll");
            if (modelFiles.Length > 0)
            {
                modelfile = modelFiles[0];
            }
            else
            {
                throw new Exception($"Not Found model_xx.dll @{Define.BuildOutputDir}");
            }

            int random = RandomGenerator.RandomNumber(100000000, 999999999);
            string logicFile = $"Hotfix_{random}";

            List<string> codes;
            switch (globalConfig.CodeMode)
            {
                case CodeMode.Client:
                    codes = new List<string>()
                    {
                        "Assets/Scripts/Codes/Hotfix/",
                        "Assets/Scripts/Codes/HotfixView/Client/",
                    };
                    break;
                default:
                    throw new Exception("unknow 'CodeMode'");
            }
            var depends = new List<string>(GetBatlleDlls());
            depends.Add(modelfile);
            BuildMuteAssembly(logicFile, codes, depends.ToArray(), codeOptimization, globalConfig.CodeMode);

            File.Copy(Path.Combine(Define.BuildOutputDir, $"{logicFile}.dll"), Path.Combine(CodeDir, $"Hotfix.dll.bytes"), true);
            File.Copy(Path.Combine(Define.BuildOutputDir, $"{logicFile}.pdb"), Path.Combine(CodeDir, $"Hotfix.pdb.bytes"), true);
            Debug.Log("copy Hotfix.dll to Assets/Res/CodeDll success!");
        }

        private static List<string> GetBatlleDlls()
        {
            if(RefBattleDlls.Count == 0)
            {
                var manifest = AssetDatabase.LoadAssetAtPath("Assets/Res/Config/HotupdateBattleDlls.asset", typeof(ConfigStringList)) as ConfigStringList;
                if (manifest == null)
                {
                    throw new Exception($"Assets/Res/Config/HotupdateBattleDlls 配置不存在");
                }

                foreach (var dll in manifest.List)
                {
                    RefBattleDlls.Add(Path.Combine(BattleDllDir, dll));
                }
            }

            return RefBattleDlls;
        }

        public static void BuildMuteAssembly(
            string assemblyName, List<string> CodeDirectorys,
            string[] additionalReferences, CodeOptimization codeOptimization, CodeMode codeMode = CodeMode.Client)
        {
            if (!Directory.Exists(Define.BuildOutputDir))
            {
                Directory.CreateDirectory(Define.BuildOutputDir);
            }

            List<string> scripts = new List<string>();
            for (int i = 0; i < CodeDirectorys.Count; i++)
            {
                DirectoryInfo dti = new DirectoryInfo(CodeDirectorys[i]);
                FileInfo[] fileInfos = dti.GetFiles("*.cs", System.IO.SearchOption.AllDirectories);
                for (int j = 0; j < fileInfos.Length; j++)
                {
                    scripts.Add(fileInfos[j].FullName);
                }
            }

            string dllPath = Path.Combine(Define.BuildOutputDir, $"{assemblyName}.dll");
            string pdbPath = Path.Combine(Define.BuildOutputDir, $"{assemblyName}.pdb");
            File.Delete(dllPath);
            File.Delete(pdbPath);

            Directory.CreateDirectory(Define.BuildOutputDir);

            AssemblyBuilder assemblyBuilder = new AssemblyBuilder(dllPath, scripts.ToArray());

            if (codeMode == CodeMode.Client)
            {
                assemblyBuilder.excludeReferences = new string[]
                {
                    "DnsClient.dll",
                    "MongoDB.Driver.Core.dll",
                    "MongoDB.Driver.dll",
                    "MongoDB.Driver.Legacy.dll",
                    "MongoDB.Libmongocrypt.dll",
                    "SharpCompress.dll",
                    "System.Buffers.dll",
                    "System.Runtime.CompilerServices.Unsafe.dll",
                    "System.Text.Encoding.CodePages.dll"
                };
            }

            //启用UnSafe
            assemblyBuilder.compilerOptions.AllowUnsafeCode = true;

            BuildTargetGroup buildTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget);

            assemblyBuilder.compilerOptions.CodeOptimization = codeOptimization;
            assemblyBuilder.compilerOptions.ApiCompatibilityLevel = PlayerSettings.GetApiCompatibilityLevel(buildTargetGroup);
            // assemblyBuilder.compilerOptions.ApiCompatibilityLevel = ApiCompatibilityLevel.NET_4_6;

            assemblyBuilder.additionalReferences = additionalReferences;

            assemblyBuilder.flags = AssemblyBuilderFlags.None;
            //AssemblyBuilderFlags.None                 正常发布
            //AssemblyBuilderFlags.DevelopmentBuild     开发模式打包
            //AssemblyBuilderFlags.EditorAssembly       编辑器状态
            assemblyBuilder.referencesOptions = ReferencesOptions.UseEngineModules;

            assemblyBuilder.buildTarget = EditorUserBuildSettings.activeBuildTarget;

            assemblyBuilder.buildTargetGroup = buildTargetGroup;

            assemblyBuilder.buildStarted += assemblyPath => Debug.LogFormat("build start:" + assemblyPath);

            assemblyBuilder.buildFinished += (assemblyPath, compilerMessages) =>
            {
                int errorCount = compilerMessages.Count(m => m.type == CompilerMessageType.Error);
                int warningCount = compilerMessages.Count(m => m.type == CompilerMessageType.Warning);

                Debug.LogFormat("Warnings: {0} - Errors: {1}", warningCount, errorCount);

                if (warningCount > 0)
                {
                    Debug.LogFormat("有{0}个Warning!!!", warningCount);
                    for (int i = 0; i < compilerMessages.Length; i++)
                    {
                        if (compilerMessages[i].type == CompilerMessageType.Warning)
                        {
                            Debug.LogWarning(compilerMessages[i].message);
                        }
                    }
                }

                if (errorCount > 0)
                {
                    for (int i = 0; i < compilerMessages.Length; i++)
                    {
                        if (compilerMessages[i].type == CompilerMessageType.Error)
                        {
                            string filename = compilerMessages[i].file;
                            if(filename.IsNullOrWhitespace())
                            {
                                Debug.LogError( compilerMessages[i].message);
                            }
                            else
                            {
                                filename = Path.GetFullPath(filename);
                                Debug.LogError(
                                $"{compilerMessages[i].message} (at <a href=\"file:///{filename}/\" line=\"{compilerMessages[i].line}\">{Path.GetFileName(filename)}</a>)");
                            }
                        }
                    }
                }
            };

            //开始构建
            if (!assemblyBuilder.Build())
            {
                Debug.LogErrorFormat("build fail:" + assemblyBuilder.assemblyPath);
                return;
            }

            while (EditorApplication.isCompiling)
            {
                // 主线程sleep并不影响编译线程
                Thread.Sleep(1);
            }
        }
    }
}