using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using PNGCompression;
using QGMiniGame;
using UnityEditor;
using UnityEngine;
using xgame.Zip;
using CompressionLevel = xgame.Zlib.CompressionLevel;

namespace TyphoonUnitySDK
{
    /// <summary>
    /// vivo mini 发布逻辑
    /// </summary>
    public class PublishVivoMini : IPublish
    {
        private const string OUT_PUT_ROOT = "typhoon_vivo";
        private const string CDN_FOLDER = "typhoon_vivo/cdn_res";

        public void Publish(PublishSetting setting)
        {
            var result = new PublishResult();
            var config = VivoMiniConfig.Default;
            if (string.IsNullOrWhiteSpace(config.PackageName))
            {
                UniEditor.ShowMessageBox("包名不可为空", null);
                return;
            }

            if (string.IsNullOrWhiteSpace(config.GameName))
            {
                UniEditor.ShowMessageBox("游戏名不可为空", null);
                return;
            }

            //检查配置
            if (string.IsNullOrWhiteSpace(config.PrivatePem))
            {
                UniEditor.ShowMessageBox("preivate.pem 不可为空", null);
                return;
            }

            if (!File.Exists(config.PrivatePem))
            {
                UniEditor.ShowMessageBox($"找不到preivate.pem 路径:{config.PrivatePem}", null);
                return;
            }

            if (string.IsNullOrWhiteSpace(config.CertificatePem))
            {
                UniEditor.ShowMessageBox("certificate.pem 不可为空", null);
                return;
            }

            if (!File.Exists(config.CertificatePem))
            {
                UniEditor.ShowMessageBox($"找不到certificate.pem 路径:{config.CertificatePem}", null);
                return;
            }

            if (config.PrivacyPolicyFile == null)
            {
                UniEditor.ShowMessageBox($"隐私政策不可为空", null);
                return;
            }

            var path = GetAgeImagePath(config.AgeLevel);
            if (!File.Exists(path))
            {
                UniEditor.ShowMessageBox($"适龄提醒配置有误,请重新配置", null);
                return;
            }

            if (config.EnableTrack && string.IsNullOrWhiteSpace(config.UmaAppKey))
            {
                UniEditor.ShowMessageBox($"友盟appkey不可为空,请重新配置", null);
                return;
            }


            //检查nodejs_v13
            if (!PluginModuleInstaller.IsInstallNodeV13())
            {
                UniEditor.ShowMessageBox($"缺少nodejs-v10.13 安装？\n(请等待安装完毕后再重试)",
                    () => { PluginModuleInstaller.InstallNodeV13Async(); });
                return;
            }

            //检查node_modules
            if (!PluginModuleInstaller.IsInstallVivoNodeModule())
            {
                UniEditor.ShowMessageBox($"缺少vivo_node_modules 安装？\n(请等待安装完毕后再重试)",
                    () => { PluginModuleInstaller.InstallVivoNodeModuleAsync(); });
                return;
            }

            DoBuild(setting);
        }


        public void DoBuild(PublishSetting setting)
        {
            var buildSrc = Path.GetFullPath(OUT_PUT_ROOT);
            var wasmUrl = string.Empty;
            var streamingAssetsUrl = string.Empty;
            var webglVivoPath = $"{OUT_PUT_ROOT}/webgl_vivo";
            try
            {
                Debug.Log($"删除：{webglVivoPath}");
                if (Directory.Exists(webglVivoPath))
                {
                    UniEditor.DeleteFolder(webglVivoPath);
                }
            }
            catch (Exception e)
            {
                throw new Exception($"删除文件夹失败，请手动删除：{OUT_PUT_ROOT},{e}");
            }

            QGGameTools.setEditorConfig(buildSrc, wasmUrl, streamingAssetsUrl, string.Empty, string.Empty,
                string.Empty, string.Empty, string.Empty, false, false, false);
            var webGlPath = Path.Combine(buildSrc, QGEditorWindow.webglDir);
            QGGameTools.SetPlayer();
            QGGameTools.BuildWebGL(webGlPath);
            if (!Directory.Exists(webGlPath))
            {
                throw new Exception($"构建失败，WebGl项目未成功生成,找不到：{webGlPath}");
                return;
            }

            QGGameTools.CreateEnvConfig(wasmUrl, streamingAssetsUrl, webGlPath, new ArrayList(), false);
            QGGameTools.ConvetWebGl(buildSrc, webGlPath, false, false);

            var projPath = $"{buildSrc}/webgl_vivo";
            if (Directory.Exists(projPath))
            {
                ModifyExportProj(projPath, setting);
            }
        }


        //修改导出工程，加入sdk
        public void ModifyExportProj(string projPath, PublishSetting setting)
        {
            Debug.Log("组合vivo小游戏工程...");
            var config = VivoMiniConfig.Default;
            //webgl文件夹
            var webglFolderPath = $"{Path.GetDirectoryName(projPath)}/webgl";
            var game_js_path = $"{projPath}/src/game.js";
            var engine_path = $"{projPath}/engine";
            var minigame_config_js_path = $"{projPath}/minigame.config.js";
            if (FoldersExists(projPath, engine_path) && FilesExists(game_js_path))
            {
                {
                    //typhoon-unity-vivo.js
                    var from = $"Assets/Typhoon_Gen/TyphoonSDK/Runtime/VivoMini/Editor/typhoon-unity-vivo.js";
                    var to = $"{engine_path}/{Path.GetFileName(from)}";
                    Debug.Log($"复制{from}到{to}");
                    File.Copy(from, to, true);
                }


                {
                    //typhoon-mini-sdk-vivo.js
                    var from = $"Assets/Typhoon_Gen/TyphoonSDK/Runtime/VivoMini/Editor/typhoon-mini-sdk-vivo.js";
                    var to = $"{engine_path}/{Path.GetFileName(from)}";
                    Debug.Log($"复制{from}到{to}");
                    File.Copy(from, to, true);
                }


                {
                    //typhoon-unity-vivo-config.js
                    var from = $"Assets/Typhoon_Gen/TyphoonSDK/Runtime/VivoMini/Editor/typhoon-unity-vivo-config.js";
                    var to = $"{engine_path}/{Path.GetFileName(from)}";
                    var code = File.ReadAllText(from);
                    code = code.Replace("$LIMIT_FPS_FLAG$", config.LimitFps ? "true" : "false");
                    code = code.Replace("$LIMIT_FPS$", config.FrameRate.ToString());
                    //广告参数
                    code = code.Replace("$BANNER_ENABLE$", config.BannerEnable.ToString().ToLower());
                    code = code.Replace("$BANNER_INTERVAL$", config.BannerInterval.ToString());
                    code = code.Replace("$BANNER_POS_ID$", $"\"{config.BannerPosId}\"");
                    code = code.Replace("$BANNER_BOTTOM$", config.BannerBottom ? "1" : "0");

                    code = code.Replace("$INTERS_ENABLE$", config.IntersEnable.ToString().ToLower());
                    code = code.Replace("$INTERS_COOL_DOWN$", config.IntersInterval.ToString());
                    code = code.Replace("$INTERS_POS_ID$", $"\"{config.IntersPosId}\"");

                    code = code.Replace("$VIDEO_ENABLE$", config.VideoEnable.ToString().ToLower());
                    code = code.Replace("$VIDEO_RESET_INTERS_COOL$", config.VideoResetIntersCool.ToString());
                    code = code.Replace("$VIDEO_POS_ID$", $"\"{config.VideoPosId}\"");

                    //隐私政策
                    var privacy = File.ReadAllText(AssetDatabase.GetAssetPath(config.PrivacyPolicyFile));
                    code = code.Replace("$PRIVACY_MESSAGE$", $"{privacy}");

                    //事件上报
                    code = code.Replace("$ENABLE_TRACK$", config.EnableTrack ? "1" : "0");
                    code = code.Replace("$UMA_APP_KEY$", config.UmaAppKey);

                    Debug.Log($"写入{to}");
                    File.WriteAllText(to, code);
                }


                {
                    //game.js
                    var from = "Assets/Typhoon_Gen/TyphoonSDK/Runtime/VivoMini/Editor/game.js";
                    var to = game_js_path;
                    Debug.Log($"复制{from}到{to}");
                    File.Copy(from, to, true);
                }


                {
                    //minigame.config.js
                    var from = "Assets/Typhoon_Gen/TyphoonSDK/Runtime/VivoMini/Editor/minigame.config.js";
                    var to = minigame_config_js_path;
                    Debug.Log($"复制{from}到{to}");
                    File.Copy(from, to, true);
                }


                {
                    //写入uma.min.js
                    var from = "Assets/Typhoon_Gen/TyphoonSDK/Runtime/VivoMini/Editor/uma.min.js";
                    var to = $"{engine_path}/{Path.GetFileName(from)}";
                    var code = File.ReadAllText(from);
                    var pattern = @"export[\s]*{[\s]*ge[\s]*as[\s]*default[\s]*}[\s]*;";
                    var replacement = $"window['umaPlugin']=ge;";
                    code = Regex.Replace(code, pattern, replacement);
                    Debug.Log($"写入{to}");
                    File.WriteAllText(to, code);
                }


                //复制签名到指定目录
                Debug.Log("复制签名到sign/release");
                var signFolder = $"{projPath}/sign/release";
                if (Directory.Exists(signFolder))
                {
                    Directory.Delete(signFolder, true);
                }

                Directory.CreateDirectory(signFolder);
                var privatePemToPath = $"{signFolder}/private.pem";
                var certificatePemToPath = $"{signFolder}/certificate.pem";
                File.Copy(config.PrivatePem, privatePemToPath, true);
                File.Copy(config.CertificatePem, certificatePemToPath, true);
                //修改版本号等信息
                Debug.Log("修改manifest.json");
                //webgl_vivo\src\manifest.json
                var manifestJson = File.ReadAllText($"{projPath}/src/manifest.json");
                var manifest = manifestJson.ToXObject<VivoManifestJsonData>();
                manifest.package = config.PackageName;
                manifest.name = config.GameName;
                manifest.minPlatformVersion = "1090";
                manifest.deviceOrientation =
                    config.ScreenOrientation == Orientation.Portrait ? "portrait" : "landscape";
                manifest.versionCode = config.VersionCode.ToString();
                manifest.versionName = config.VersionName;
                //覆写manifest
                File.WriteAllText($"{projPath}/src/manifest.json", manifest.ToXJson());
                if (config.GameIcon != null)
                {
                    var logoPath = AssetDatabase.GetAssetPath(config.GameIcon);
                    //复制icon到指定文件
                    File.Copy(logoPath, $"{projPath}/src/image/logo.png", true);
                }

                //生成加载背景
                var background = config.LoadBackground != null
                    ? AssetDatabase.GetAssetPath(config.LoadBackground)
                    : "Assets/Typhoon_Gen/TyphoonSDK/Runtime/VivoMini/Editor/black4x4.png";
                var ageImage = $"{UniEditor.PathRoot}/Editor/Texture/age_{(int)config.AgeLevel}.png";
                var bgPath = $"{projPath}/src/image/background.png";
                Debug.Log($"组合生成加载背景图...{background}+{ageImage}-->{bgPath}");
                GenerateLoadingBg(bgPath, background, ageImage);


                //修改webgl.json
                Debug.Log("修改webgl.json");
                var webgljsonPath = $"{projPath}/buildUnity/webgl.json";
                if (File.Exists(webgljsonPath))
                {
                    var txt = File.ReadAllText(webgljsonPath);
                    txt = txt.Replace("webgl.data.unityweb", "online_mini.data.unityweb");
                    txt = txt.Replace("webgl.wasm.code.unityweb", "online_mini.wasm.code.unityweb");
                    //覆写
                    File.WriteAllText(webgljsonPath, txt);
                }

                var unity2021OrNew = false;
#if UNITY_2020_1_OR_NEWER
                unity2021OrNew = true;
#endif
                if (unity2021OrNew)
                {
                    //修改webgl.wasm.framework.unityweb
                    var path = Path.GetFullPath($"{projPath}/buildUnity/webgl.wasm.framework.unityweb");
                    var txt = File.ReadAllText(path);
                    StringBuilder stringBuilder = new StringBuilder();
                    stringBuilder.Append(
                        "var qgAbortController = {signal: {aborted: true,reason: {},abort: function () {},timeout: function () {}},abort: function (reason) {}}");
                    stringBuilder.AppendLine();
                    stringBuilder.Append(txt);
                    stringBuilder.Replace($"new AbortController", "qgAbortController");
                    File.WriteAllText(path, stringBuilder.ToString());
                }

                //修改env.conf
                Debug.Log("修改env.conf");
                var envPath = $"{projPath}/buildUnity/env.conf";
                var sb = new StringBuilder();
                sb.Append("{");
                sb.Append($"\"wasmUrl\":\"{config.GetFinalCDN()}/uwb.zip\",");
                var streamAssetsFolderPath = $"{webglFolderPath}/StreamingAssets";
                if (Directory.Exists(streamAssetsFolderPath))
                {
                    //如果存在StreamingAssets文件夹
                    sb.Append($"\"streamingAssetsUrl\":\"{config.GetFinalCDN()}/StreamingAssets\",");
                }
                else
                {
                    sb.Append($"\"streamingAssetsUrl\":\"\",");
                }

                sb.Append($"\"preloadUrl\":\"\"");
                sb.Append("}");
                File.WriteAllText(envPath, sb.ToString());

                //创建CDN资源文件夹
                var cdnFolderPath = $"{CDN_FOLDER}/v{config.VersionCode}";
                Debug.Log($"生成cdn资源文件--->{cdnFolderPath}");
                if (!Directory.Exists(cdnFolderPath))
                {
                    Directory.CreateDirectory(cdnFolderPath);
                }

                //创建cdn资源
                var uwbzipPath = $"{cdnFolderPath}/uwb.zip";
                var streamAssetsPastePath = $"{cdnFolderPath}/StreamingAssets";
                var webgl_data_unityweb = $"{projPath}/buildUnity/webgl.data.unityweb";
                var webgl_wasm_code_unityweb = $"{projPath}/buildUnity/webgl.wasm.code.unityweb";

                var online_data_unityweb = $"{projPath}/buildUnity/online_mini.data.unityweb";
                var online_wasm_code_unityweb = $"{projPath}/buildUnity/online_mini.wasm.code.unityweb";

                //创建uwb.zip

                if (FilesExists(webgl_data_unityweb, webgl_wasm_code_unityweb))
                {
                    Debug.Log($"创建uwb.zip--->{uwbzipPath}");
                    //创建online_mini.data.unityweb，online_mini.wasm.code.unityweb
                    File.Copy(webgl_data_unityweb, online_data_unityweb, true);
                    File.Copy(webgl_wasm_code_unityweb, online_wasm_code_unityweb, true);
                    if (File.Exists(uwbzipPath))
                    {
                        File.Delete(uwbzipPath);
                    }

                    //生成uwb.zip
                    using (ZipFile zip = new ZipFile(uwbzipPath, Encoding.Default))
                    {
                        zip.AddItem(online_data_unityweb, "");
                        zip.AddItem(online_wasm_code_unityweb, "");
#if !UNITY_2021_1_OR_NEWER
                        zip.CompressionLevel = CompressionLevel.BestCompression;
#endif
                        zip.Save();
                    }

                    //删除多余文件
                    File.Delete(webgl_data_unityweb);
                    File.Delete(webgl_wasm_code_unityweb);
                    File.Delete(online_data_unityweb);
                    File.Delete(online_wasm_code_unityweb);
                }

                //创建StreamingAssets
                if (Directory.Exists(streamAssetsFolderPath))
                {
                    Debug.Log($"复制StreamingAssets--->{streamAssetsPastePath}");
                    UniEditor.CopyFolder(streamAssetsFolderPath, streamAssetsPastePath);
                }

                //创建使用说明
                File.WriteAllText($"{cdnFolderPath}/说明.txt",
                    $"上传所有文件到cdn地址:{config.GetFinalCDN()}\n下载地址如：\"{config.GetFinalCDN()}/uwb.zip\"");

                var nodejs_v13 = PluginModuleInstaller.GetPluginInfo(PluginModuleInstaller.PluginNames.NodeJsV10_13);
                var nodejs_v13_root = nodejs_v13.RootPath;
                nodejs_v13_root = nodejs_v13_root.Replace("/", "\\");
                var rpk_release = $"{projPath}/#生成rpk-release.bat";
                Action finalStep = () =>
                {
                    File.WriteAllText($"{projPath}/#生成rpk-debug.bat",
                        $"{nodejs_v13_root}\\npm.cmd run build");
                    {
                        var content = $@"
call {nodejs_v13_root}\npm.cmd run release
if %errorlevel% equ 0 (
    echo Build completed successfully.
    call explorer .\dist
    exit
) else (
    echo Build encountered an error.
    pause
)";
                        File.WriteAllText(rpk_release, content);
                    }

                    File.WriteAllText($"{projPath}/#run-server.bat",
                        $"{nodejs_v13_root}\\npm.cmd run server");

                    File.Copy($"Assets/Typhoon_Gen/TyphoonSDK/Runtime/VivoMini/Editor/发布说明.txt", $"{projPath}/发布说明.txt",
                        true);

                    // var readmePath = $"{projPath}/发布说明.txt";
                    if (File.Exists(rpk_release))
                    {
                        Debug.Log($"生成：{rpk_release},请留意打开文件夹");
                        if (Directory.Exists(cdnFolderPath))
                        {
                            EditorUtility.RevealInFinder(cdnFolderPath);
                        }

                        EditorUtility.RevealInFinder($"{projPath}/#生成rpk-release.bat");
                    }
                };

                //判断node_modules是否完整
                var node_modules_folder = $"{projPath}/node_modules";
                if (!Directory.Exists(node_modules_folder))
                {
                    //查看缓存路径
                    var vivo_node_modules =
                        $"{PluginModuleInstaller.GetPluginRootPath(PluginModuleInstaller.PluginNames.VivoNodeModule)}/node_modules";
                    Action nextStep = () =>
                    {
                        Debug.Log($"补充node_modules...(从：{vivo_node_modules}->{node_modules_folder})");
                        UniEditor.CopyFolderWithProcess(vivo_node_modules, node_modules_folder,
                            (process) =>
                            {
                                EditorUtility.DisplayProgressBar("补充node_modules...",
                                    $"请稍等...{(process * 100).ToString("F1")}%", process);
                            },
                            () =>
                            {
                                EditorUtility.ClearProgressBar();
                                finalStep();
                            });
                    };
                    if (!Directory.Exists(vivo_node_modules))
                    {
                        throw new Exception($"找不到模块：{vivo_node_modules}");
                    }
                    else
                    {
                        nextStep();
                    }
                }
                else
                {
                    //存在node_modules模块,执行最后一步
                    finalStep();
                }
            }
            else
            {
                throw new Exception("发布失败,小游戏工程目录不完整");
            }
        }


        //判断文件夹是否存在
        public bool FoldersExists(params string[] folders)
        {
            foreach (var folder in folders)
            {
                if (!Directory.Exists(folder))
                {
                    return false;
                }
            }

            return true;
        }

        //检查文件是否存在
        public bool FilesExists(params string[] files)
        {
            foreach (var folder in files)
            {
                if (!File.Exists(folder))
                {
                    return false;
                }
            }

            return true;
        }


        private string GetAgeImagePath(AgeLevel ageLevel)
        {
            return $"{UniEditor.PathRoot}/Editor/Texture/age_{(int)ageLevel}.png";
        }

        //生成加载背景图
        private void GenerateLoadingBg(string output, string imgBg, string imgAge)
        {
            var texBg = ReadImage(imgBg);
            var texAge = ReadImage(imgAge);
            var width = 512;
            var height = 1024;
            texBg = ScaleImage(texBg, width, height);
            var ageSize = 128;
            var rectBg = new Rect(0, 0, width, height);
            var rectAge = new Rect(0, 0, ageSize, ageSize);
            var rectArea = new Rect(Mathf.Lerp(0, rectBg.width, 0.6f), Mathf.Lerp(0, rectBg.height, 0.25f),
                rectBg.width * 0.5f, rectBg.height * 0.125f);
            rectAge.center = rectArea.center;
            var pw = rectAge.width / rectBg.width;
            var ph = rectAge.height / rectBg.height;
            var spx = Mathf.InverseLerp(0, rectBg.xMax, rectAge.x);
            var spy = Mathf.InverseLerp(0, rectBg.yMax, rectAge.y);
            var tex = FillImageTo(texBg, texAge, spx, spy, pw, ph);
            var data = tex.EncodeToPNG();
            File.WriteAllBytes(output, data);
            CompressPng(output);
        }


        private static Texture2D ReadImage(string file)
        {
            var tex = AssetDatabase.LoadAssetAtPath<Texture2D>(file);
            var bytes = File.ReadAllBytes(file);
            var img = new Texture2D(tex.width, tex.height);
            img.alphaIsTransparency = true;
            img.LoadImage(bytes);
            img.Apply();
            return img;
        }


        //采样图片到指定大小
        private static Color[] RemapImageTo(Texture2D sourceImage, int width, int height)
        {
            // 计算图片的采样比例
            float widthRatio = (float)width / sourceImage.width;
            float heightRatio = (float)height / sourceImage.height;
            var colors = new Color[width * height];
            // 遍历目标图片的像素，并根据采样比例从源图片中采样颜色
            var index = 0;
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int sourceX = Mathf.RoundToInt(x / widthRatio);
                    int sourceY = Mathf.RoundToInt(y / heightRatio);
                    Color sourceColor = sourceImage.GetPixel(sourceX, sourceY);
                    colors[index] = sourceColor;
                    index += 1;
                }
            }

            return colors;
        }

        private static Texture2D ScaleImage(Texture2D image, int width, int height)
        {
            var tex = new Texture2D(width, height);
            tex.alphaIsTransparency = true;
            var colors = RemapImageTo(image, width, height);
            tex.SetPixels(colors);
            tex.Apply();
            return tex;
        }

        //填充图片A到B指定区域
        private static Texture2D FillImageTo(Texture2D sourceImage, Texture2D fillImage, float spx, float spy, float pw,
            float ph)
        {
            Texture2D result = new Texture2D(sourceImage.width, sourceImage.height);
            result.SetPixels(sourceImage.GetPixels());
            result.alphaIsTransparency = true;
            var pixelX = (int)(Mathf.Lerp(0, sourceImage.width, spx));
            var pixelY = (int)(Mathf.Lerp(0, sourceImage.height, spy));
            var fillWidth = (int)(pw * sourceImage.width);
            var fillHeight = (int)(ph * sourceImage.height);
            var colors = RemapImageTo(fillImage, fillWidth, fillHeight);
            //剔除alpha
            var original = result.GetPixels(pixelX, pixelY, fillWidth, fillHeight);
            var blendColors = BlendColorsWithAlpha(original, colors);
            result.SetPixels(pixelX, pixelY, fillWidth, fillHeight, blendColors);
            result.Apply();
            return result;
        }


        //颜色融合
        public static Color[] BlendColorsWithAlpha(Color[] colorArrayA, Color[] colorArrayB)
        {
            int length = colorArrayA.Length;
            Color[] blendedColors = new Color[length];

            for (int i = 0; i < length; i++)
            {
                Color colorA = colorArrayA[i];
                Color colorB = colorArrayB[i];

                if (colorB.a == 0)
                {
                    blendedColors[i] = colorA;
                }
                else
                {
                    Color blendedColor = new Color(
                        (colorA.r * (1 - colorB.a)) + (colorB.r * colorB.a),
                        (colorA.g * (1 - colorB.a)) + (colorB.g * colorB.a),
                        (colorA.b * (1 - colorB.a)) + (colorB.b * colorB.a),
                        (colorA.a * (1 - colorB.a)) + (colorB.a * colorB.a)
                    );

                    blendedColors[i] = blendedColor;
                }
            }

            return blendedColors;
        }

        //压缩图片
        private static void CompressPng(string path)
        {
            var pngCompressor = new PNGCompressor();
            Debug.Log($"压缩：{path}");
            var tmp = $"{path}.tmp";
            File.Copy(path, tmp, true);
            pngCompressor.CompressImageLossy(tmp, path);
            File.Delete(tmp);
            Debug.Log($"压缩：{path} [完毕]");
        }
    }
}