using System.Collections.Generic;
using UnityEngine;
using XGame.Editor.Build.AssetBundles;
using XGame.Framework.Json;
using XFADefine = XGame.Framework.Asset.Define;

namespace XGame.Editor.Build
{
    public struct AbEncryptInfo
    {
        public long bundleId;

        public string originBundleName;

        public uint offset;
    }

    [BuildCommand((uint)BuildCommandPriority.ExportAssets)]
    class CmdExportAssets : BaseBuildCommand, ICommandExecuter
    {
        #region copy assets

        /// <summary>
        /// 异或加密使用的key值：1023
        /// </summary>
        const int encryptKey = 0x03FF;

        Dictionary<string, AbEncryptInfo> CollectAbEncryptInfos()
        {
            var abEncryptInfos = new Dictionary<string, AbEncryptInfo>();
            var abmanifest = AssetBundleBackupManifest.Load();
            if (abmanifest != null)
            {
                //manifest包的加密信息
                var encrypt = new AbEncryptInfo()
                {
                    bundleId = long.Parse(XFADefine.MANIFEST_BUNDLE_NAME),
                    originBundleName = XFADefine.MANIFEST_BUNDLE_NAME,
                    offset = XFADefine.MANIFEST_BUNDLE_OFFSET
                };
                abEncryptInfos.Add(XFADefine.MANIFEST_BUNDLE_NAME, encrypt);

                var abInfos = abmanifest.bundleInfos;
                foreach (var abInfo in abInfos)
                {
                    encrypt = new AbEncryptInfo()
                    {
                        bundleId = abInfo.bundleId,
                        originBundleName = abInfo.nameOrGuid,
                        offset = abInfo.offset
                    };
                    abEncryptInfos.Add(encrypt.bundleId.ToString(), encrypt);
                }
            }
            return abEncryptInfos;
        }
        private Dictionary<string, string> CollectBundleCrc()
        {
            var bundleCrcMap = new Dictionary<string, string>();
            var manifestPath = Asset.PathDefine.AssetBundleOutputPath + "/BundleCrcInfoManifest.manifest";
            if (System.IO.File.Exists(manifestPath))
            {
                var text = System.IO.File.ReadAllText(manifestPath);
                var manifest = XJson.ToObject<BundleCrcInfoManifest>(text);
                if (manifest.bundleInfos != null)
                {
                    foreach (var info in manifest.bundleInfos)
                    {
                        bundleCrcMap.Add(info.bundleName, info.bundleCRC);
                    }
                }
            }
            return bundleCrcMap;
        }

        byte[] ReadAllBytes(string assetPath, Dictionary<string, AbEncryptInfo> abEncryptInfos)
        {
            byte[] bytes = System.IO.File.ReadAllBytes(assetPath);
            if (assetPath.Contains(Asset.PathDefine.AssetBundleOutputPath))
            {
                var fileName = System.IO.Path.GetFileNameWithoutExtension(assetPath);
                if (!abEncryptInfos.TryGetValue(fileName, out var encryptInfo))
                    return bytes;

                if (encryptInfo.offset <= 0) return bytes;

                var encryptData = System.Text.Encoding.UTF8.GetBytes(encryptInfo.originBundleName);
                for (var i = 0; i < encryptData.Length; i++) // 依次对字符串中各字符进行操作
                {
                    var current = encryptData[i];
                    encryptData[i] = (byte)(current ^ encryptKey); // 将密钥与字符异或
                }

                //var fileData = System.IO.File.ReadAllBytes(assetPath);
                var length = bytes.Length + encryptInfo.offset;

                var buffer = new byte[length];
                System.Array.Copy(encryptData, 0, buffer, 0, encryptInfo.offset);
                System.Array.Copy(bytes, 0, buffer, encryptInfo.offset, bytes.Length);
                return buffer;
            }
            return bytes;
        }

        void WriteAllBytes(string path, byte[] bytes)
        {
            var destDir = Asset.FileUtil.GetDirectoryName(path, true);
            if (!System.IO.Directory.Exists(destDir))
                System.IO.Directory.CreateDirectory(destDir);
            System.IO.File.WriteAllBytes(path, bytes);
        }

        void CopyAssets(string streamingAssetsPath, Dictionary<string, string> assetMap, Dictionary<string, AbEncryptInfo> abEncryptInfos)
        {
            foreach (var item in assetMap)
            {
                string assetPath = item.Key;

                if (!System.IO.File.Exists(assetPath))
                {
                    BuildLog.Error($"Asset can not found：{assetPath}");
                    return;
                }

                byte[] bytes = ReadAllBytes(assetPath, abEncryptInfos);

                string toPath = System.IO.Path.Combine(streamingAssetsPath, item.Value);
                WriteAllBytes(toPath, bytes);
            }
        }

        #endregion

        //#region split assets

        //void CollectionExpansionAssetInfos(ProductAssetsContext assetsContext, Dictionary<string, string> assetMap, out Dictionary<string, EditorAssetInfo> assetInfos, out CustomPackageInfo[] customPackageInfos)
        //{
        //    assetInfos = new Dictionary<string, EditorAssetInfo>(assetMap.Count);
        //    foreach (var item in assetMap)
        //    {
        //        EditorAssetInfo assetInfo = new EditorAssetInfo();
        //        assetInfo.packageId = ManifestUtilities.MainAssetId;
        //        assetInfo.path = item.Value;
        //        assetInfos.Add(item.Key, assetInfo);
        //    }

        //    if (Context.config.ignoreSplitAssets)
        //    {
        //        customPackageInfos = null;
        //        return;
        //    }

        //    CustomPackageControl customPackageControl = new CustomPackageControl(assetsContext);
        //    customPackageControl.CollectPackages();
        //    customPackageInfos = customPackageControl.GeneratePackages();

        //    Dictionary<string, string> assetPkgMap = new Dictionary<string, string>();
        //    if (null != customPackageInfos && customPackageInfos.Length > 0)
        //    {
        //        foreach (var pkgInfo in customPackageInfos)
        //        {
        //            var errors = customPackageControl.GetPackageErrors(pkgInfo.uid);
        //            if (null != errors && errors.Length > 0)
        //            {
        //                foreach (var error in errors)
        //                {
        //                    BuildLog.Warn("[GenExpancePackageManifest] discover error asset! uid: {0}, id: {1}, error code:{2}, is ext asset: {3} addressable: {4}\nerror asset path:{5}\nsource:{6}"
        //                        , pkgInfo.uid, pkgInfo.packageID, error.code, error.asset.isExtAsset, error.asset.addressable, error.asset.assetPath, error.asset.source);
        //                }
        //            }

        //            foreach (var path in pkgInfo.assetPaths)
        //            {
        //                assetPkgMap.Add(path, pkgInfo.packageID);
        //            }
        //        }
        //    }

        //    foreach (var item in assetPkgMap)
        //    {
        //        if (!assetInfos.TryGetValue(item.Key, out var assetInfo))
        //        {
        //            BuildLog.Warn($"Expansion path not found in assets: {item.Key}");
        //            continue;
        //        }
        //        assetInfo.packageId = item.Value;
        //    }
        //}

        //void SplitAssets(string outputDir, string streamingAssetsPath, Dictionary<string, EditorAssetInfo> assetInfos)
        //{
        //    foreach (var assetInfo in assetInfos.Values)
        //    {
        //        bool isMainAsset = assetInfo.packageId == ManifestUtilities.MainAssetId || string.IsNullOrEmpty(assetInfo.packageId);
        //        string toPath = GetOutputPath(outputDir, assetInfo.path, isMainAsset, assetInfo.packageId);

        //        var destDir = KAFileUtil.GetDirectoryName(toPath, true);
        //        if (!System.IO.Directory.Exists(destDir)) System.IO.Directory.CreateDirectory(destDir);

        //        string fromPath = System.IO.Path.Combine(streamingAssetsPath, assetInfo.path);
        //        if (isMainAsset)
        //            System.IO.File.Copy(fromPath, toPath);
        //        else
        //            System.IO.File.Move(fromPath, toPath);
        //    }
        //}

        //#endregion

        //#region generate runtime-configs

        //ExpansionPackageManifest GenerateUniquePackageManifest(string outputDir, string streamingAssetsPath, CustomPackageInfo[] customPackageInfos, ref Dictionary<string, EditorAssetInfo> assetInfos)
        //{
        //    if (null == customPackageInfos || customPackageInfos.Length <= 0) return null;

        //    List<ExpansionPackageInfo> infos = new List<ExpansionPackageInfo>();
        //    foreach (var item in customPackageInfos)
        //    {
        //        if (item.isShared) continue;
        //        if ((null == item.assetPaths || item.assetPaths.Length <= 0) && (null == item.dependencies || item.dependencies.Length <= 0)) continue;

        //        var info = new ExpansionPackageInfo()
        //        {
        //            downloadMode = item.downloadMode,
        //            id = item.packageID,
        //            priority = item.priority,
        //            name = item.packageName,
        //            updateMode = item.updateMode,
        //        };

        //        info.dependencies = new string[item.dependencies.Length];
        //        item.dependencies.CopyTo(info.dependencies, 0);
        //        infos.Add(info);
        //    }

        //    ExpansionPackageManifest manifest = new ExpansionPackageManifest();
        //    manifest.packageInfos = infos.ToArray();

        //    string outputPath = System.IO.Path.Combine(outputDir, "packages", "main", "assets");
        //    ManifestUtilities.SaveUniquePackageManifest(outputPath, manifest);

        //    string fromPath = System.IO.Path.Combine(outputPath, ManifestUtilities.AssetManifestRelativePath, "UniquePackages.manifest");
        //    string toDir = System.IO.Path.Combine(streamingAssetsPath, ManifestUtilities.AssetManifestRelativePath);
        //    if (!System.IO.Directory.Exists(toDir)) System.IO.Directory.CreateDirectory(toDir);
        //    string toPath = System.IO.Path.Combine(toDir, "UniquePackages.manifest");
        //    System.IO.File.Copy(fromPath, toPath);

        //    EditorAssetInfo assetInfo = new EditorAssetInfo();
        //    assetInfo.packageId = ManifestUtilities.MainAssetId;
        //    assetInfo.path = System.IO.Path.Combine(ManifestUtilities.AssetManifestRelativePath, /*ManifestUtilities.UniquePackagesManifestFileName*/"UniquePackages.manifest").Replace('\\', '/');
        //    assetInfos.Add(assetInfo.path, assetInfo);
        //    Debug.Log($"GenerateUniquePackageManifest. assetInfo.path {assetInfo.path} , assetInfo {assetInfo.packageId}");
        //    return manifest;
        //}

        //void GenerateAssetManifest(string outputDir, string streamingAssetsPath, ref Dictionary<string, EditorAssetInfo> assetInfos, CustomPackageInfo[] customPackageInfos)
        //{
        //    List<AssetInfo> list = new List<AssetInfo>();
        //    foreach (var assetInfo in assetInfos.Values)
        //    {
        //        bool isMainAsset = assetInfo.packageId == ManifestUtilities.MainAssetId || string.IsNullOrEmpty(assetInfo.packageId);
        //        if (!isMainAsset) continue;

        //        string path = GetOutputPath(outputDir, assetInfo.path, true, null);

        //        AssetInfo info = new AssetInfo();
        //        info.relativePath = assetInfo.path.Replace('\\', '/');
        //        info.checkCodeType = ECheckCodeType.MD5;
        //        info.size = new System.IO.FileInfo(path).Length;
        //        info.checkCode = AssetUtilities.GetBinaryCheckCode(path, ECheckCodeType.MD5);
        //        list.Add(info);
        //    }
        //    AssetManifest mainManifest = new AssetManifest();
        //    mainManifest.assetInfos = list.ToArray();

        //    string assetDir = System.IO.Path.Combine(outputDir, "packages", "main", "assets");
        //    ManifestUtilities.SaveMainAssetManifest(assetDir, mainManifest);

        //    string mainManifestFileName = $"{ManifestUtilities.MainAssetId}.{ManifestUtilities.ManifestExtensionName}";
        //    string fromPath = System.IO.Path.Combine(assetDir, ManifestUtilities.AssetManifestRelativePath, mainManifestFileName);
        //    string toDir = System.IO.Path.Combine(streamingAssetsPath, ManifestUtilities.AssetManifestRelativePath);
        //    if (!System.IO.Directory.Exists(toDir)) System.IO.Directory.CreateDirectory(toDir);
        //    string toPath = System.IO.Path.Combine(toDir, mainManifestFileName);
        //    System.IO.File.Copy(fromPath, toPath);

        //    EditorAssetInfo editorAssetInfo = new EditorAssetInfo();
        //    editorAssetInfo.packageId = ManifestUtilities.MainAssetId;
        //    editorAssetInfo.path = System.IO.Path.Combine(ManifestUtilities.AssetManifestRelativePath, mainManifestFileName);
        //    assetInfos.Add(editorAssetInfo.path, editorAssetInfo);

        //    if (null == customPackageInfos || customPackageInfos.Length <= 0) return;

        //    foreach (var pkgInfo in customPackageInfos)
        //    {
        //        if ((null == pkgInfo.assetPaths || pkgInfo.assetPaths.Length <= 0) && (null == pkgInfo.dependencies || pkgInfo.dependencies.Length <= 0)) continue;

        //        list.Clear();
        //        foreach (var path in pkgInfo.assetPaths)
        //        {
        //            if (!assetInfos.TryGetValue(path, out var assetInfo))
        //            {
        //                BuildLog.Warn("Can't found asset: {0}, package: {1}", path, pkgInfo.packageID);
        //                continue;
        //            }

        //            string outputPath = GetOutputPath(outputDir, assetInfo.path, false, pkgInfo.packageID);

        //            AssetInfo info = new AssetInfo();
        //            info.relativePath = assetInfo.path.Replace('\\', '/');
        //            info.checkCodeType = ECheckCodeType.MD5;
        //            info.size = new System.IO.FileInfo(outputPath).Length;
        //            info.checkCode = AssetUtilities.GetBinaryCheckCode(outputPath, ECheckCodeType.MD5);
        //            list.Add(info);
        //        }

        //        AssetManifest manifest = new AssetManifest();
        //        manifest.assetInfos = list.ToArray();

        //        assetDir = System.IO.Path.Combine(outputDir, "packages", pkgInfo.packageID, "assets");
        //        ManifestUtilities.SaveExpansionAssetManifest(assetDir, pkgInfo.packageID, manifest);

        //        editorAssetInfo = new EditorAssetInfo();
        //        editorAssetInfo.packageId = pkgInfo.packageID;
        //        editorAssetInfo.path = System.IO.Path.Combine(ManifestUtilities.AssetManifestRelativePath, $"{pkgInfo.packageID}.{ManifestUtilities.ManifestExtensionName}");
        //        assetInfos.Add(editorAssetInfo.path, editorAssetInfo);
        //    }
        //}

        //#endregion

        //#region generate backend-configs

        //void CollectPlacardAssets(HashSet<string> innerBundles, string dirPath, int platformTag, string streamAsstesDir, ref List<string> list)
        //{
        //    var dirs = System.IO.Directory.GetDirectories(dirPath);
        //    foreach (var dir in dirs)
        //    {
        //        var subPlatformTag = platformTag;
        //        var entryPath = dir.Replace('\\', '/');

        //        if (subPlatformTag != 1) subPlatformTag = KAFileUtil.VerifyPlatformPath(entryPath);
        //        if (subPlatformTag == -1) continue;

        //        CollectPlacardAssets(innerBundles, dir, platformTag, streamAsstesDir, ref list);
        //    }

        //    string[] files = System.IO.Directory.GetFiles(dirPath);
        //    foreach (var file in files)
        //    {
        //        if (AssetUtilities.IsIgnorFile(file)) continue;
        //        if (!(AssetUtilities.IsInnerAsset(file, AssetUtilities.PlacardTag) || innerBundles.Contains(file))) continue;

        //        var entryPath = file.Replace('\\', '/');
        //        string relativePath;
        //        if (platformTag == 1)
        //        {
        //            //当前平台资源
        //            var platformName = PlatformUtil.ActivePlatform.ToString();
        //            relativePath = entryPath.Substring(entryPath.IndexOf(platformName) + platformName.Length + 1);
        //        }
        //        else
        //        {
        //            relativePath = entryPath.Substring(streamAsstesDir.Length + 1);
        //        }
        //        list.Add(relativePath);
        //    }
        //}

        //void GeneratePlacardManifest(string backendConfigDir, ProductAssetsContext assetsContext)
        //{
        //    var innerBundles = assetsContext.CollectPlacardBundles();

        //    string streamAsstesDir = Kailash.Framework.Adapter.Driven.FileSystem.FileUtil.WorkPath.Replace('\\', '/');
        //    List<string> placardAssets = new List<string>();
        //    CollectPlacardAssets(innerBundles, streamAsstesDir, 0, streamAsstesDir, ref placardAssets);

        //    List<PlacardInfo> placardInfos = new List<PlacardInfo>();
        //    foreach (var item in placardAssets)
        //    {
        //        placardInfos.Add(new PlacardInfo() { filePath = item });
        //    }

        //    var manifest = new PlacardManifest();
        //    manifest.placardInfos = placardInfos.ToArray();

        //    string json = KailashJson.ToJson(manifest);
        //    string path = System.IO.Path.Combine(backendConfigDir, "PlacardManifest.txt");
        //    System.IO.File.WriteAllText(path, json);
        //}

        //void CalculateHotUpdateCheckCode(string outputDir, Dictionary<string, EditorAssetInfo> assetInfos)
        //{
        //    var bundleCrcMap = CollectBundleCrc();
        //    var regex = new Regex(@"CRC: (\d+)", RegexOptions.Multiline);
        //    foreach (var item in assetInfos)
        //    {
        //        string ext = System.IO.Path.GetExtension(item.Key);
        //        if (ext != ".bundle")
        //        {
        //            bool isMainAsset = item.Value.packageId == ManifestUtilities.MainAssetId || string.IsNullOrEmpty(item.Value.packageId);
        //            string filePath = GetOutputPath(outputDir, item.Value.path, isMainAsset, item.Value.packageId);
        //            item.Value.checkCode = KailashNative.GetMD5(filePath);
        //        }
        //        else
        //        {
        //            if (bundleCrcMap.TryGetValue(System.IO.Path.GetFileNameWithoutExtension(item.Key), out var bundleCrc))
        //            {
        //                item.Value.checkCode = bundleCrc;
        //            }
        //            else
        //            {
        //                string path = string.Format("{0}.manifest", item.Key);
        //                if (System.IO.File.Exists(path))
        //                {
        //                    string content = System.IO.File.ReadAllText(path);
        //                    var match = regex.Match(content);
        //                    item.Value.checkCode = match.Groups[1].Value;
        //                }
        //                else
        //                {
        //                    BuildLog.Error($"CalculateHotUpdateCheckCode:找不到CRC配置信息。Path:{item.Key}");
        //                }
        //            }
        //        }
        //    }
        //}

        //#endregion

        string GetOutputPath(string outputDir, string relativePath, bool isMainAsset, string packageId)
        {
            return isMainAsset
                ? System.IO.Path.Combine(outputDir, "packages", "main", "assets", relativePath)
                : System.IO.Path.Combine(outputDir, "packages", packageId, "assets", relativePath);
        }

        BuildErrorCode ICommandExecuter.Execute()
        {
            var streamingAssetsPath = Application.streamingAssetsPath.Replace('\\', '/');
            if (System.IO.Directory.Exists(streamingAssetsPath))
            {
                UnityEditor.FileUtil.DeleteFileOrDirectory(streamingAssetsPath);
                UnityEditor.AssetDatabase.Refresh();
            }
            var pkgPath = System.IO.Path.Combine(Context.config.project.outputPath, "packages");
            if (System.IO.Directory.Exists(pkgPath))
            {
                UnityEditor.FileUtil.DeleteFileOrDirectory(pkgPath);
            }
            // 拷贝资源
            Dictionary<string, string> assetMap = AssetUtils.CollectionAllAssets();
            Dictionary<string, AbEncryptInfo> abEncryptInfos = CollectAbEncryptInfos();
            CopyAssets(streamingAssetsPath, assetMap, abEncryptInfos);
            //if (Context.config.editor.isNative)
            {
                //本地打包拷贝完资源即可退出
                UnityEditor.AssetDatabase.Refresh();
                return BuildErrorCode.CmdCompleted;
            }

            //var assetsContext = new ProductAssetsContext();
            //assetsContext.Init();

            //// 拆分分包资源
            //CollectionExpansionAssetInfos(assetsContext, assetMap, out Dictionary<string, EditorAssetInfo> assetInfos, out CustomPackageInfo[] customPackageInfos);
            //SplitAssets(Context.config.project.outputPath, streamingAssetsPath, assetInfos); // 移动或拷贝资源

            //// 生成专属包配置清单（Runtime）：用于运行时，对分包资源进行操作
            //ExpansionPackageManifest manifest = GenerateUniquePackageManifest(Context.config.project.outputPath, streamingAssetsPath, customPackageInfos, ref assetInfos);

            //// 生成分包资源文件清单（包含主包）（Runtime）：用于运行时，对分包资源进行校验
            //GenerateAssetManifest(Context.config.project.outputPath, streamingAssetsPath, ref assetInfos, customPackageInfos);

            //// 生成海报配置（Editor）：用于制作更新包时，单独提取海报相关资源
            //string backendConfigDir = System.IO.Path.Combine(Context.config.project.outputPath, "backend-configs");
            //if (!System.IO.Directory.Exists(backendConfigDir)) System.IO.Directory.CreateDirectory(backendConfigDir);
            //GeneratePlacardManifest(backendConfigDir, assetsContext);

            //// 生成专属包配置清单（Editor）：用于上传分包时，上传相关的分包信息
            //string json = XJson.ToJson(manifest);
            //string path = System.IO.Path.Combine(backendConfigDir, "UniquePackages.txt");
            //System.IO.File.WriteAllText(path, json);

            //// 生成所有资源清单：用于制作更新包时，对资源进行对比
            //CalculateHotUpdateCheckCode(Context.config.project.outputPath, assetInfos);


            //List<EditorAssetInfo> infos = new List<EditorAssetInfo>();
            //foreach (var info in assetInfos.Values)
            //{
            //    infos.Add(info);
            //}
            //EditorAssetInfoManifest editorAssetInfoManifest = new EditorAssetInfoManifest();
            //editorAssetInfoManifest.assetInfos = infos.ToArray();
            //json = XJson.ToJson(editorAssetInfoManifest);
            //path = System.IO.Path.Combine(backendConfigDir, "AssetsManifest.txt");
            //System.IO.File.WriteAllText(path, json);
            ////由于分包会将资源从streamingAssetsPath移出，因此在最后才调用Refresh()
            //UnityEditor.AssetDatabase.Refresh();
            //return BuildErrorCode.CmdCompleted;
        }
    }
}
