using System.Collections.Generic;
using System.Collections;
using System;
using System.IO;
using LuaFramework;
using System.Security.Cryptography.X509Certificates;
using System.Net;
using System.Net.Security;
using UnityEngine;
using UnityEngine.Networking;
using LitJson;
using Ionic.Zip;

public class HotUpdater
{
    /// <summary>
    /// app整包强制更新
    /// </summary>
    public Action actionForceFullAppUpdate;
    /// <summary>
    /// app整包非强制更新
    /// </summary>
    public Action actionWeakFullAppUpdate;
    /// <summary>
    /// 无任何更新
    /// </summary>
    public Action actionNothingToUpdate;
    /// <summary>
    /// 全部热更新下载完毕
    /// </summary>
    public Action actionAllDownloadDone;
    /// <summary>
    /// 错误提示
    /// </summary>
    public Action<UnityWebRequest.Result> actionShowErrorTips;
    /// <summary>
    /// 更新提示语
    /// </summary>
    public Action<string> actionUpdateTipsText;
    /// <summary>
    /// 更新进度
    /// </summary>
    public Action<float> actionUpdateProgress;

    private DownloadingInfo mDownloadingInfo;

    /// <summary>
    /// 需要整包更新
    /// </summary>
    private bool mNeedFullAppUpdate;
    /// <summary>
    /// 是否强制增量更新
    /// </summary>
    private bool mHasNextUpdate;
    /// <summary>
    /// 整包更新的url
    /// </summary>
    private string mFullAppUpdateUrl;
    /// <summary>
    /// 相同app版本号的res更新（res版本号增加）
    /// </summary>
    private bool mSameAppVerResUpdate;

    private List<PackageInfo> mPackageList = new List<PackageInfo>();

    /// <summary>
    /// 下载器
    /// </summary>
    private Downloader mDownloader;
    /// <summary>
    /// 下载器协程
    /// </summary>
    private IEnumerator mOnDownloadItr;

    private int mCurPackageIndex = 0;

    public void Init()
    {
        // 解决HTTPS证书的问题
        ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;
    }

    public void Start()
    {
        mNeedFullAppUpdate = false;
        mHasNextUpdate = true;
        mFullAppUpdateUrl = null;
        mSameAppVerResUpdate = false;
        mPackageList.Clear();
        mDownloadingInfo = new DownloadingInfo();

        // 请求更新列表
        var updateInfos = ReqUpdateInfo();
        if (null != updateInfos)
        {
            // 对更新列表进行排序
            SortUpdateInfo(ref updateInfos);
            // 计算实际需要下载的增量包列表
            CalculateUpdatePackageList(updateInfos);
            // 需要整包更新
            if (mNeedFullAppUpdate)
            {
                if (mHasNextUpdate)
                {
                    // 可以不强制更新整包，有【下次】按钮
                    actionWeakFullAppUpdate?.Invoke();
                }
                else
                {
                    // 必须整包更新，强制，没有【下次】按钮
                    actionForceFullAppUpdate?.Invoke();
                }
            }
            else if (mSameAppVerResUpdate)
            {
                // 有同app版本的res增量包更新
                mCurPackageIndex = 0;
                StartDownloadResPack();
            }
            else
            {
                // 没有任何更新
                actionNothingToUpdate?.Invoke();
            }
        }
    }

    /// <summary>
    /// 请求更新信息
    /// </summary>
    /// <returns></returns>
    private List<UpdateInfo> ReqUpdateInfo()
    {
        UnityWebRequest unityWebRequest = UnityWebRequest.Get(AppConst.WebUrl + "update_list.json");
        var request = unityWebRequest.SendWebRequest();
        while (!request.isDone) { }
        if (!string.IsNullOrEmpty(unityWebRequest.error))
        {
            if (!string.IsNullOrEmpty(unityWebRequest.error))
            {
                // GameLogger.LogError(unityWebRequest.error);
                actionShowErrorTips.Invoke(unityWebRequest.result);
                return null;
            }
        }

        GameLogger.Log(unityWebRequest.downloadHandler.text);
        return JsonMapper.ToObject<List<UpdateInfo>>(unityWebRequest.downloadHandler.text);
    }

    private void SortUpdateInfo(ref List<UpdateInfo> updateInfos)
    {
        // 逆序排序，版本号大的排前面
        updateInfos.Sort((a, b) => {
            return VersionMgr.CompareVersion(a.appVersion, b.appVersion);
        });
    }

    /// <summary>
    /// 分析更新类型
    /// </summary>
    /// <param name="updateInfos"></param>
    private void CalculateUpdatePackageList(List<UpdateInfo> updateInfos)
    {
        // 最大的app版本号
        string biggestAppVersion = "0.0.0.0";
        mHasNextUpdate = false;
        foreach (var info in updateInfos)
        {
            if (VersionMgr.CompareVersion(info.appVersion, VersionMgr.instance.appVersion) > 0)
            {
                // 列表版本号大于当前版本号，进行全量更新
                mNeedFullAppUpdate = true;
                if (VersionMgr.CompareVersion(info.appVersion, biggestAppVersion) > 0)
                {
                    biggestAppVersion = info.appVersion;
                    mFullAppUpdateUrl = info.appUrl;
                }
            }
            if (VersionMgr.CompareVersion(info.appVersion, VersionMgr.instance.appVersion) == 0)
            {
                // 列表版本号等于当前版本号，进行增量更新
                mHasNextUpdate = true;
                foreach (var package in info.updateList)
                {
                    if (VersionMgr.CompareVersion(package.resVersion, VersionMgr.instance.resVersion) > 0)
                    {
                        // 提升资源版本号，加入更新列表中
                        mSameAppVerResUpdate = true;
                        mPackageList.Add(package);
                    }
                }
            }
        }
        GameLogger.Log("mPackageList.Count = " + mPackageList.Count);
    }

    /// <summary>
    /// 跳转应用商店进行整包更新
    /// </summary>
    public void DoFullAppUpdate()
    {
        Application.OpenURL(mFullAppUpdateUrl);
    }

    /// <summary>
    /// 跳过大版本更新
    /// </summary>
    public void DoNextTime()
    {
        // 不更新整包，执行res更新
        if (mSameAppVerResUpdate)
        {
            mCurPackageIndex = 0;
            StartDownloadResPack();
        }
        else
        {
            actionNothingToUpdate?.Invoke();
        }
    }

    /// <summary>
    /// 开始下载资源包
    /// </summary>
    /// <param name="next">是否有下一个资源包</param>
    private void StartDownloadResPack(bool next = false)
    {
        actionUpdateTipsText?.Invoke("正在下载更新包，请稍等。。。");
        if (next)
            ++mCurPackageIndex;
        if (mCurPackageIndex > mPackageList.Count - 1)
        {
            GameLogger.Log("全部热更包下载完毕");
            actionAllDownloadDone?.Invoke();
            return;
        }

        // 排序，确保版本号小的先下载
        mPackageList.Sort((a, b) => {
            return VersionMgr.CompareVersion(a.resVersion, b.resVersion);
        });

        var packageInfo = mPackageList[mCurPackageIndex];
        GameLogger.Log("开始下载：" + packageInfo.ToString());
        mDownloadingInfo.totalPackageCount = mPackageList.Count;
        mDownloadingInfo.packIndex = mCurPackageIndex;
        mDownloadingInfo.targetDownloadSize = packageInfo.size;
        mDownloader = new Downloader();
        mDownloader.Start(packageInfo);
    }

    public void Update()
    {
        if (null != mDownloader)
        {
            switch (mDownloader.state)
            {
                case Downloader.DownloadState.End:
                    {
                        mDownloader = null;
                        mOnDownloadItr = OnDownloadEnd();
                    }
                    break;
                case Downloader.DownloadState.Ing:
                    {
                        OnDownloadIng();
                    }
                    break;
                case Downloader.DownloadState.ConnectionError:
                    {
                        mDownloader = null;
                        OnDownloadError(UnityWebRequest.Result.ConnectionError);
                    }
                    break;
                case Downloader.DownloadState.DataProcessingError:
                    {
                        mDownloader = null;
                        OnDownloadError(UnityWebRequest.Result.DataProcessingError);
                    }
                    break;
            }
        }
        RunCoroutine();
    }

    private void RunCoroutine()
    {
        if (null != mOnDownloadItr && !mOnDownloadItr.MoveNext())
        {
            mOnDownloadItr = null;
        }
    }

    /// <summary>
    /// 下载完毕
    /// </summary>
    /// <returns></returns>
    private IEnumerator OnDownloadEnd()
    {
        var packageInfo = mPackageList[mCurPackageIndex];
        GameLogger.Log("下载完毕：" + packageInfo.url);
        actionUpdateTipsText?.Invoke("正在校验文件，请稍等。。。");
        yield return null;

        var filePath = Application.persistentDataPath + "/" + packageInfo.md5;
        var md5Ok = CheckMD5(filePath, packageInfo.md5);
        if (!md5Ok)
        {
            DeleteFile(filePath);
            GameLogger.LogError("MD5校验不通过，重新下载");
            actionUpdateTipsText?.Invoke("正在重新下载更新包，请稍等。。。");
            mDownloader = new Downloader();
            mDownloader.Start(mPackageList[mCurPackageIndex]);
        }
        else
        {
            // 解压zip
            actionUpdateTipsText?.Invoke("正在解压文件，请稍等。。。");
            yield return null;
            GameLogger.LogGreen("解压文件：" + filePath);
            int index = 0;
            using (ZipFile zip = new ZipFile(filePath))
            {
                var cnt = zip.Count;
                foreach (var entity in zip)
                {
                    ++index;
                    // 解压缩
                    entity.Extract(Application.persistentDataPath + "/update/", ExtractExistingFileAction.OverwriteSilently);
                    actionUpdateProgress?.Invoke((float)index / cnt);
                    yield return null;
                }
            }
            // 删除zip
            DeleteFile(filePath);
            // 更新版本号
            VersionMgr.instance.UpdateResVersion(packageInfo.resVersion);
            // 请求下载下一个压缩包
            StartDownloadResPack(true);
        }
    }

    /// <summary>
    /// 下载中
    /// </summary>
    private void OnDownloadIng()
    {
        // 更新进度条
        mDownloadingInfo.curDownloadSize = mDownloader.curDownloadSize;
        var value = (float)mDownloadingInfo.curDownloadSize / mDownloadingInfo.targetDownloadSize;
        actionUpdateProgress?.Invoke(value);
    }

    /// <summary>
    /// 下载异常
    /// </summary>
    private void OnDownloadError(UnityWebRequest.Result result)
    {
        actionShowErrorTips?.Invoke(result);
    }

    /// <summary>
    /// 删除文件
    /// </summary>
    /// <param name="path">路径</param>
    private void DeleteFile(string path)
    {
        GameLogger.LogGreen("删除文件：" + path);
        if (File.Exists(path))
            File.Delete(path);
    }

    /// <summary>
    /// 检测md5
    /// </summary>
    /// <param name="path">路径</param>
    /// <param name="md5">md5值</param>
    /// <returns></returns>
    private bool CheckMD5(string path, string md5)
    {
        GameLogger.Log("CheckMD5, path = " + path + ", md5 = " + md5);
        var localMD5 = LuaFramework.Util.md5file(path);
        GameLogger.Log("localMD5 = " + localMD5);
        var ok = localMD5.Equals(md5.ToLower());
        GameLogger.LogGreen("MD5校验，ok = " + ok);
        return ok;
    }

    /// <summary>
    /// 解决HTTPS证书问题
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="certificate"></param>
    /// <param name="chain"></param>
    /// <param name="sslPolicyErrors"></param>
    /// <returns></returns>
    private bool MyRemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None) return true;
        bool isOk = true;
        for (int i = 0; i < chain.ChainStatus.Length; i++)
        {
            if (chain.ChainStatus[i].Status != X509ChainStatusFlags.RevocationStatusUnknown)
            {
                chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
                chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
                chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 1, 0);
                chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
                bool chainIsValid = chain.Build((X509Certificate2)certificate);
                if (!chainIsValid)
                {
                    isOk = false;
                    break;
                }
            }
        }
        return isOk;
    }

    public void Dispose()
    {
        if (null != mDownloader)
            mDownloader.Dispose();
    }

    public class UpdateInfo
    {
        public string appVersion;
        public string appUrl;
        public List<PackageInfo> updateList;
    }

    public class PackageInfo
    {
        public string resVersion;
        public string md5;
        public int size;
        public string url;
        public override string ToString()
        {
            return string.Format("resVersion = {0}, md5 = {1}, size = {2}, url = {3}", resVersion, md5, size, url);
        }
    }

    public class DownloadingInfo
    {
        public long curDownloadSize;
        public long targetDownloadSize;
        public int packIndex;
        public int totalPackageCount;
    }
}