using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using asap.core;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;

namespace tysdk
{
    public partial class TYSdkFacade : MonoBehaviour
    {
        public class AccountInfo
        {
            public int userId;
            public string token;
            public string jwtToken;
            public string strUserId => userId.ToString();
            public HashSet<EAccoutType> linkedAccout = new HashSet<EAccoutType>();
            public EAccoutType channel { get; set; }
            public string userName;
            public string avatar;

            private const string KEY_ACCOUNT_INFO = "KEY_ACCOUNT_INFO";

            public void AddLinkedAccount(EAccoutType accoutType)
            {
                if (!linkedAccout.Contains(accoutType))
                {
                    linkedAccout.Add(accoutType);
                }
            }

            private static Dictionary<string, EAccoutType> accoutTypeMap = new Dictionary<string, EAccoutType>(){
                {"google", EAccoutType.hwGoogle},
                {"fb", EAccoutType.hwFacebook},
                {"tyGuest", EAccoutType.hwGuest},
                {"ios13", EAccoutType.Apple}
            };

            private static EAccoutType ConvertAccoutType(string accountType)
            {
                if (accoutTypeMap.ContainsKey(accountType))
                {
                    return accoutTypeMap[accountType];
                }
                return (EAccoutType)Enum.Parse(typeof(EAccoutType), accountType);
            }

            public void Save()
            {
                JObject jInfo = new JObject();
                jInfo["channel"] = channel.ToString();
                jInfo["token"] = token;
                jInfo["userId"] = userId;
                jInfo["userName"] = userName;
                jInfo["avatar"] = avatar;
                jInfo["jwtToken"] = jwtToken;
                jInfo["linkedAccout"] = string.Join(",", linkedAccout.Select(x => x.ToString()));
                PlayerPrefs.SetString(KEY_ACCOUNT_INFO, jInfo.ToString());
            }

            public static void Remove()
            {
                PlayerPrefs.DeleteKey(KEY_ACCOUNT_INFO);
            }

            public static AccountInfo GetSavedAccoutInfo()
            {
                if (PlayerPrefs.HasKey(KEY_ACCOUNT_INFO))
                {
                    var jInfo = JObject.Parse(PlayerPrefs.GetString(KEY_ACCOUNT_INFO));
                    var accountInfo = new AccountInfo()
                    {
                        userId = (int)jInfo["userId"],
                        token = (string)jInfo["token"],
                        channel = ConvertAccoutType((string)jInfo["channel"]),
                        jwtToken = (string)jInfo["jwtToken"],
                        linkedAccout = jInfo["linkedAccout"].ToString().Split(',').Select(x => ConvertAccoutType(x)).ToHashSet()
                    };

                    if (jInfo.TryGetValue("userName", out var userName))
                    {
                        accountInfo.userName = (string)userName;
                    }

                    if (jInfo.TryGetValue("avatar", out var avatar))
                    {
                        accountInfo.avatar = (string)avatar;
                    }

                    return accountInfo;
                }
                else
                {
                    return null;
                }
            }
        }

        private static AccountInfo _accountInfo;
        public static AccountInfo TYAccountInfo => _accountInfo;

        private static TYSdkFacade _instance;
        public static TYSdkFacade Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new GameObject("TYSdkFacade").AddComponent<TYSdkFacade>();
                    DontDestroyOnLoad(_instance.gameObject);
                }
                return _instance;
            }
        }
        public void Init()
        {
#if UNITY_ANDROID
            UnityBridgeFunc.InitSDK();
#endif

        }


        /*================================================

                                 _                             _   
                                / \   ___ ___ ___  _   _ _ __ | |_ 
                               / _ \ / __/ __/ _ \| | | | '_ \| __|
                              / ___ \ (_| (_| (_) | |_| | | | | |_ 
                             /_/   \_\___\___\___/ \__,_|_| |_|\__|

        =================================================*/


        public static bool IsLoggedIn => _accountInfo != null;

        public void Logout()
        {
            UnityEngine.Debug.Log("Logout");
            if (_accountInfo != null)
            {
                UnityBridgeFunc.UnityLogOutByChannel(_accountInfo.channel);
            }

            _accountInfo = null;
            AccountInfo.Remove();
        }

        public async Task<LoginInfo> Login(EAccoutType accoutType)
        {
#if UNITY_EDITOR 
            await Task.Yield();
            var deviceId = UnityEngine.Device.SystemInfo.deviceUniqueIdentifier.Split('-')[0].Substring(0, 8);

            var userId = (int)ulong.Parse(deviceId, System.Globalization.NumberStyles.HexNumber);

            _accountInfo = new AccountInfo()
            {
                userId = userId,
            };

            return new LoginInfo()
            {
                isSuccess = true,
                userId = userId
            };

#elif UNITY_ANDROID || UNITY_IOS
            var task = TYSDKCallbackManager.Instance.RegisterCallback<LoginInfo>(TimeSpan.FromSeconds(30));

            UnityBridgeFunc.UnityLogin(accoutType);

            try
            {
              var loginInfo = await task;
              if(loginInfo.isSuccess)
              {
                  _accountInfo.channel = accoutType;
                  _accountInfo.AddLinkedAccount(accoutType);
                  _accountInfo.Save();
              }
              return loginInfo;
            }
            catch(Exception e)
            {
              Debug.LogWarning($"[TYSdkFacade] Login failed: {e}");
              return new LoginInfo(){isSuccess = false};
            }
#endif
        }

        public async Task<LoginInfo> LoginByToken()
        {
            var accoutInfo = AccountInfo.GetSavedAccoutInfo();
            if (accoutInfo == null) return new LoginInfo();

            var task = TYSDKCallbackManager.Instance.RegisterCallback<LoginInfo>(TimeSpan.FromSeconds(30));

            UnityBridgeFunc.UnityLoginByTokenFun(accoutInfo.token);

            try
            {
                var loginInfo = await task;
                if (loginInfo.isSuccess)
                {
                    _accountInfo.Save();
                }
                return loginInfo;
            }
            catch (Exception e)
            {
                Debug.LogWarning($"[TYSdkFacade] Login by token failed: {e}");
                return new LoginInfo() { isSuccess = false };
            }
        }
        
        public async Task<LoginInfo> LoginBySns()
        {
            var accountInfo = AccountInfo.GetSavedAccoutInfo();
            if (accountInfo == null) return new LoginInfo();

            var task = TYSDKCallbackManager.Instance.RegisterCallback<LoginInfo>(TimeSpan.FromSeconds(30));

            var snsType = UnityBridgeFunc.UnityLoginBySnsInfoFun();

            try
            {
                var loginInfo = await task;
                if (loginInfo.isSuccess)
                {
                    if (Enum.TryParse<EAccoutType>(snsType, out var accountType))
                    {
                        _accountInfo.channel = accountType;
                    }
                    _accountInfo.AddLinkedAccount(accountType);
                    _accountInfo.Save();
                }
                return loginInfo;
            }
            catch (Exception e)
            {
                Debug.LogWarning($"[TYSdkFacade] Login by token failed: {e}");
                return new LoginInfo() { isSuccess = false };
            }
        }

        public void LoginResult(string json)
        {
            Debug.Log("[TYSdkFacade] Login callback:");
            var result = new LoginInfo();
            var data = JObject.Parse(json);
            var code = (int)data["code"];
            result.code = code;

            if (code == 0)
            {
                var loginData = data["respObj"]["result"];

                var userId = (int)loginData["userId"];
                var token = (string)loginData["token"];
                var jwtToken = (string)loginData["jwttoken"];

                var savedInfo = AccountInfo.GetSavedAccoutInfo();
                if (savedInfo == null || savedInfo.userId != userId)
                {
                    _accountInfo = new AccountInfo()
                    {
                        userId = (int)loginData["userId"],
                        userName = (string)loginData["userName"],
                        avatar = (string)loginData["purl"],
                        token = (string)loginData["token"],
                        jwtToken = (string)loginData["jwttoken"],
                    };
                }
                else
                {
                    savedInfo.token = token;
                    savedInfo.jwtToken = jwtToken;
                    _accountInfo = savedInfo;
                }

                result.isSuccess = true;
                result.userId = _accountInfo.userId;
                SetUserInfo();
            }

            TYSDKCallbackManager.Instance.TryTriggerCallback(result);
        }

        private void SetUserInfo()
        {
            if (_accountInfo == null) return;
            var strUserId = _accountInfo.strUserId;

            UnityBridgeFunc.SetGaUserInfo(strUserId);
        }


#if UNITY_EDITOR
        public async Task<LinkResult> LinkAccount(EAccoutType accoutType)
        {
            await Task.Delay(300);
            return new LinkResult(){code = 1};
        }
#elif UNITY_IOS || UNITY_ANDROID
      public async Task<LinkResult> LinkAccount(EAccoutType accoutType)
        {
            if (_accountInfo == null) return new LinkResult(){ code = 1};
            if (_accountInfo.linkedAccout.Contains(accoutType)) return new LinkResult(){ code = 0};

            var task = TYSDKCallbackManager.Instance.RegisterCallback<LinkResult>(TimeSpan.FromMinutes(1));
            UnityBridgeFunc.LinkAccount(accoutType);

            try
            {
                var result = await task;
                if (result.code == 0)
                {
                    if (_accountInfo == null)
                        _accountInfo = AccountInfo.GetSavedAccoutInfo();

                    if (_accountInfo != null)
                    {
                        _accountInfo.AddLinkedAccount(accoutType);
                        _accountInfo.Save();
                    }
                }
                return result;
            }
            catch (Exception e)
            {
                Debug.LogWarning($"[TYSdkFacade] Link account failed: {e}");
                return new LinkResult() { code = 1 };
            }
        }

        public void LinkAccoutResult(string message)
        {
            JObject data = JObject.Parse(message);
            var result = new LinkResult()
            {
                code = data["code"].Value<int>(),
                userId = data["userId"].Value<int>(),
            };

            if (data.ContainsKey("bindInfo"))
            {
                result.bindInfo = JsonConvert.DeserializeObject<Dictionary<string, string>>(data.GetValue(@"bindInfo").ToString());
            }

            TYSDKCallbackManager.Instance.TryTriggerCallback(result);
        }
#endif

        /// <summary>
        /// 检查是否已经绑定
        /// <returns>
        /// 1 已经绑定
        /// 0 未绑定
        /// -1 失败
        /// </returns>
        /// </summary>
        public async Task<bool> LinkCheck(EAccoutType accoutType)
        {
            if (_accountInfo == null) return false;
            if (_accountInfo.linkedAccout.Contains(accoutType)) return true;

            var task = TYSDKCallbackManager.Instance.RegisterCallback<LinkCheckInfo>(TimeSpan.FromSeconds(30));
            UnityBridgeFunc.LinkCheck(accoutType);

            try
            {
                var result = await task;

                if (result.code == 1 && !_accountInfo.linkedAccout.Contains(accoutType))
                {
                    _accountInfo.linkedAccout.Add(accoutType);
                    _accountInfo.Save();
                }

                if (result.code == -1)
                {
                    Debug.LogError("[TYSdkFacade] CheckLinkResult error");
                }

                return result.code == 1;
            }
            catch(Exception e)
            {
              Debug.LogWarning($"[TYSdkFacade] CheckLinkResult error: {e}");
              return false;
            }
        }

        public void CheckLinkResult(string codestr)
        {
            var result = new LinkCheckInfo();
            result.code = int.Parse(codestr);
            TYSDKCallbackManager.Instance.TryTriggerCallback(result);
        }

        public void Signout()
        {
            List<string> list = new List<string>();
            string defaultTyurl = "https://128-hwsfsdk-sdk-online01.qijihdhk.com";

            IConfig config = GContext.container.Resolve<IConfig>();
            var tyurl = config.Get<string>("AGG_SERVER", defaultTyurl);


            string tycs = tyurl == defaultTyurl ?
                "https://hwcsh.tygameworld.com/template/appCancel/note.html"
                :
                "https://customermanage-feature-test-web-test.tuyougame.cn/appCancel/note.html";

            string sdkurl = $"sdkurl={tyurl}";
            list.Add(sdkurl);

            string appid = "appid=20587";
            list.Add(appid);

#if UNITY_ANDROID
            string clientid = "Android_5.00_tyGuest,facebook.googleplay.0-hall20587.googleplay.FishingMaster";
#elif UNITY_IOS
            string clientid = "IOS_5.00_tyGuest,facebook,appStore.appStore.0-hall20587.appStore.FishingMaster";
#endif
            list.Add($"clientid={clientid}");

            string uid = $"uid={_accountInfo.userId}";
            list.Add(uid);

            string roleid = "roleid=0";
            list.Add(roleid);

            string gameid = "gameid=20587";
            list.Add(gameid);

            string cloudid = "cloudid=128";
            list.Add(cloudid);

            string gamename = "gamename=FishingTravel";
            list.Add(gamename);

            string certification = "certification=0";
            list.Add(certification);

            string ischannel = "ischannel=1";
            list.Add(ischannel);

            string tysdktoken = $"tysdktoken={_accountInfo.token}";
            list.Add(tysdktoken);

            list.Sort();

            string listStr = string.Join("&", list.ToArray());
            string sign = listStr +
                "csh-api-6dfa879490a249be9fbc92e97e4d898d-api-csh";
            //对sign进行MD5加密
            string signMd5 = CalculateMD5Hash(sign);
            string url = $"{tycs}?{listStr}&sign={signMd5}&isAbroad=1&lan=en";
            Debug.Log($"[TYSdkFacade:Signout] url \n{url}");
            Application.OpenURL(url);
        }

        private string CalculateMD5Hash(string input)
        {
            // Create a new instance of the MD5CryptoServiceProvider object.
            MD5 md5Hasher = MD5.Create();

            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }
    }
}
