using _Core;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace _Combo
{
    internal static class ComboClient
    {
        private static string gameId = "";
        private static string distro = "";
        private static string publishableKey = "";
        private static string endpoint = "";
        private static string unitySdkVersion = "";
        private static string unityVersion = "";
        private static string version = "/v1";
        public static void Setup(string gameId, string distro, string publishableKey, string endpoint, string unitySdkVersion, string unityVersion)
        {
            ComboClient.gameId = gameId;
            ComboClient.distro = distro;
            ComboClient.publishableKey = publishableKey;
            ComboClient.endpoint = endpoint;
            ComboClient.unitySdkVersion = unitySdkVersion;
            ComboClient.unityVersion = unityVersion;
        }

        public static void GetParameters(Action<ComboResponse<Dictionary<string, string>>> onResp)
        {
            var request = new GetParameters.Request
            {
                gameId = gameId,
                distro = distro
            };
            var url = RequestUrl("/client/parameters");

            HttpRequest.Get(
                new HttpRequestOptions
                {
                    url = url,
                    headers = Headers(),
                    body = request,
                    auth = new HttpBasicAuth(gameId, publishableKey),
                },
                httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString());

                    onResp.Invoke(ComboResponse<Dictionary<string, string>>.CreateResponse<GetParameters.Response>(url, httpResp, (data) => {
                        return data.parameters;
                    }));
                });
        }

        public static void GetDownloadUrl(Action<ComboResponse<string>> onResp)
        {
            var request = new GetDownloadUrl.Request
            {
                gameId = gameId,
                distro = distro
            };
            var url = RequestUrl("/client/download");

            HttpRequest.Get(
                new HttpRequestOptions
                {
                    url = url,
                    headers = Headers(),
                    body = request,
                    auth = new HttpBasicAuth(gameId, publishableKey),
                },
                httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString());

                    onResp.Invoke(ComboResponse<string>.CreateResponse<GetDownloadUrl.Response>(url, httpResp, (data) => {
                        return data.downloadUrl;
                    }));
                });
        }

        public static void SignIn(Credential credential, Action<ComboResponse<UserModel>> onResp, Action<InternalError> onError)
        {
            var idp = AttributeUtils.GetIdp(credential);
            var url = RequestUrl("/client/sign-in");

            HttpRequest.Post(
                new HttpRequestOptions
                {
                    url = url,
                    headers = Headers(),
                    body = new SignInParameters.Request
                    {
                        idp = idp,
                        credential = ToServerCredential(credential)
                    },
                    auth = new HttpBasicAuth(gameId, publishableKey),
                },
                httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString());

                    var resp = ComboResponse<UserModel>.CreateResponse<SignInParameters.Response>(url, httpResp, (data) => {
                        var userModel = new UserModel
                        {
                            comboId = data.comboId,
                            idp = idp,
                            externalId = data.externalId,
                            externalName = data.externalName,
                            identityToken = data.identityToken,
                            expiresAt = data.expiresAt,
                            realNameRequired = data.realNameRequired,
                            realNameTicket = data.realNameTicket,
                            activationRequired = data.activationRequired,
                            activationTicket = data.activationTicket,
                        };

                        if (data.fangchenmiTtl.HasValue)
                        {
                            userModel.fangchenmiTtl = data.fangchenmiTtl;
                        }

                        return userModel;
                    });

                    if(resp.IsSuccess)
                    {
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        HandleError("SignIn", resp.Error, false); 
                        onError(resp.Error);
                    }
                });
        }

        public static void ActivatePlayer(string ticket, string key, Action<ComboResponse<ActivatePlayer.Response>> onResp, Action<InternalError> onError)
        {
            var url = RequestUrl("/client/activate-player");
            var request = new ActivatePlayer.Request
            {
                ticket = ticket,
                activationKey = key
            };

            HttpRequest.Post(
                new HttpRequestOptions
                {
                    url = url,
                    headers = Headers(),
                    body = request,
                    auth = new HttpBasicAuth(gameId, publishableKey),
                },
                httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString());

                    var resp = ComboResponse<ActivatePlayer.Response>.CreateResponse(httpResp, url);

                    if(resp.IsSuccess)
                    {
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        HandleError("ActivatePlayer", resp.Error, false);
                        onError(resp.Error);
                    }
                });
        }

        public static void ConfirmOrder(string orderToken, string store, StoreContext context, Action<ComboResponse<Response>> onResp, Action<InternalError> onError)
        {
            var request = new Request
            {
                orderToken = orderToken,
                store = store,
                context = context,
            };
            var url = RequestUrl("/client/confirm-order");

            HttpRequest.Post(
                new HttpRequestOptions
                {
                    auth = new HttpBasicAuth(gameId, publishableKey),
                    url = url,
                    headers = Headers(),
                    body = request,
                }, httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString());
                    
                    var resp = ComboResponse<Response>.CreateResponse(httpResp, url);

                    if(resp.IsSuccess)
                    {
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        HandleError("ConfirmOrder", resp.Error, false);
                        onError(resp.Error);
                    }
                });
        }

        public static void OpenShortLink(string endpoint, string version, string link, Action<ComboResponse<ShortLink.Response>> onResp, Action<InternalError> onError)
        {
            var request = new ShortLink.Request();
            var url = $"{endpoint}/{version}/link/{link}";

            HttpRequest.Get(
                new HttpRequestOptions
                {
                    auth = new HttpBasicAuth(gameId, publishableKey),
                    url = url,
                    headers = Headers(),
                    body = request,
                }, httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString());

                    var resp = ComboResponse<ShortLink.Response>.CreateResponse(httpResp, url);

                    if(resp.IsSuccess)
                    {
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        HandleError("OpenShortLink", resp.Error, false);
                        onError(resp.Error);
                    }
                }
            );
        }

        public static void GetGoToLink(string endpoint, string version, string link, Action<ComboResponse<GoToLink.Response>> onResp, Action<InternalError> onError)
        {
            var request = new ShortLink.Request();
            var url = $"{endpoint}/{version}/goto/{link}";

            HttpRequest.Get(
                new HttpRequestOptions
                {
                    auth = new HttpBasicAuth(gameId, publishableKey),
                    url = url,
                    headers = Headers(),
                    body = request,
                }, httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString());

                    var resp = ComboResponse<GoToLink.Response>.CreateResponse(httpResp, url);

                    string redirectUrl = httpResp.GetRedirectLocation();
                    if (!string.IsNullOrEmpty(redirectUrl))
                    {
                        onResp.Invoke(resp);
                        return;
                    }
                    if(resp.IsSuccess)
                    {
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        HandleError("GoToLink", resp.Error, false);
                        onError(resp.Error);
                    }
                }
            );
        }

        // 请求 GET /v1/public/announcements 获取公告列表
        // comboId 为空时不传（未登录），level ≤ 0 时不传
        public static void GetAnnouncements(string comboId, int level, Action<ComboResponse<GetAnnouncements.Response>> onResp, Action<InternalError> onError)
        {
            var request = new GetAnnouncements.Request
            {
                gameId = gameId,
                distro = distro,
                lang = I18n.GetLanguageCode(),
                comboId = string.IsNullOrEmpty(comboId) ? null : comboId,
                level = level > 0 ? (int?)level : null,
            };
            var url = RequestUrl("/public/announcements");

            Log.D($"[ComboClient] GetAnnouncements request: {url} params: {request.ToJson()}");

            HttpRequest.Get(
                new HttpRequestOptions
                {
                    auth = new HttpBasicAuth(gameId, publishableKey),
                    url = url,
                    headers = Headers(),
                    body = request,
                }, httpResp =>
                {
                    var resp = ComboResponse<GetAnnouncements.Response>.CreateResponse(httpResp, url);

                    if (resp.IsSuccess)
                    {
                        Log.D($"[ComboClient] GetAnnouncements success: count={resp.Data?.announcements?.Count}");
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        Log.E($"[ComboClient] GetAnnouncements failed: {resp.Error}");
                        HandleError("GetAnnouncements", resp.Error, false);
                        onError(resp.Error);
                    }
                });
        }

        public static void CheckAnnouncements(Action<ComboResponse<CheckAnnouncements.Response>> onResp, Action<InternalError> onError, int? lastTime = null, int? lastLevel = null, string comboId = null, int? level = null)
        {
            var request = GetCheckAnnouncementsUrl(lastTime, lastLevel, comboId, level);
            string url = $"{RequestUrl("/client/check-announcements")}?{request}";

            HttpRequest.Get(
                new HttpRequestOptions
                {
                    auth = new HttpBasicAuth(gameId, publishableKey),
                    url = url,
                    headers = Headers(),
                    body = null,
                }, httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString());

                    var resp = ComboResponse<CheckAnnouncements.Response>.CreateResponse(httpResp, url);

                    if(resp.IsSuccess)
                    {
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        HandleError("CheckAnnouncements", resp.Error, false);
                        onError(resp.Error);
                    }
                }
            );
        }

        public static void LinkIdentity(string identityToken, string idp, Credential credential, Action<ComboResponse<LinkIdentity.Response>> onResp, Action<InternalError> onError)
        {
            var request = new LinkIdentity.Request
            {
                identityToken = identityToken,
                idp = idp,
                credential = ToServerCredential(credential),
            };
            var url = RequestUrl("/client/link-identity");

            HttpRequest.Post(
                new HttpRequestOptions
                {
                    auth = new HttpBasicAuth(gameId, publishableKey),
                    url = url,
                    headers = Headers(),
                    body = request,
                }, httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString()); 

                     
                    var resp = ComboResponse<LinkIdentity.Response>.CreateResponse(httpResp, url);

                    if(resp.IsSuccess)
                    {
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        HandleError("LinkIdentity", resp.Error, false);
                        onError(resp.Error);
                    }
                });
        }

        public static void IdentityExists(string idp, string externalId, Action<ComboResponse<IdentityExists.Response>> onResp, Action onError)
        {
            var request = new IdentityExists.Request
            {
                idp = idp,
                externalId = externalId,
            };
            var url = RequestUrl("/client/identity-exists");

            HttpRequest.Get(
                new HttpRequestOptions
                {
                    auth = new HttpBasicAuth(gameId, publishableKey),
                    url = url,
                    headers = Headers(),
                    body = request,
                }, httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString()); 

                     
                    var resp = ComboResponse<IdentityExists.Response>.CreateResponse(httpResp, url);

                    if(resp.IsSuccess)
                    {
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        HandleError("IdentityExists", resp.Error);
                        onError();
                    }
                });
        }

        public static void CreateComplainTicket(string identityToken, string targetType, string targetId, string targetName, string serverId, string roleId, string roleName, string context, Action<ComboResponse<CreateComplainTicket.Response>> onResp, Action<InternalError> onError)
        {
            var url = RequestUrl("/client/create-complain-ticket");

            HttpRequest.Post(
                new HttpRequestOptions
                {
                    url = url,
                    headers = Headers(),
                    body = new CreateComplainTicket.Request
                    {
                        identityToken = identityToken,
                        targetType = targetType,
                        targetId = targetId,
                        targetName = targetName,
                        serverId = serverId,
                        roleId = roleId,
                        roleName = roleName,
                        context = context
                    },
                    auth = new HttpBasicAuth(gameId, publishableKey),
                },
                httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString());
                    
                    var resp = ComboResponse<CreateComplainTicket.Response>.CreateResponse(httpResp, url);

                    if(resp.IsSuccess)
                    {
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        HandleError("CreateComplainTicket", resp.Error, false);
                        onError(resp.Error);
                    }
                });
        }

        public static void CreateGiftCodeTicket(string identityToken, int serverId, string roleId, string roleName, Action<ComboResponse<CreateGiftCodeTicket.Response>> onResp, Action<InternalError> onError)
        {
            var url = RequestUrl("/client/create-gift-code-ticket");

            HttpRequest.Post(
                new HttpRequestOptions
                {
                    url = url,
                    headers = Headers(),
                    body = new CreateGiftCodeTicket.Request
                    {
                        identityToken = identityToken,
                        serverId = serverId,
                        roleId = roleId,
                        roleName = roleName
                    },
                    auth = new HttpBasicAuth(gameId, publishableKey),
                },
                httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString());
                    
                    var resp = ComboResponse<CreateGiftCodeTicket.Response>.CreateResponse(httpResp, url);

                    if(resp.IsSuccess)
                    {
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        HandleError("CreateGiftCodeTicket", resp.Error, false);
                        onError(resp.Error);
                    }
                });
        }

        public static void RedeemGiftCode(string identityToken, string giftCode, int serverId, string roleId, string roleName, Action<ComboResponse<RedeemGiftCode.Response>> onResp, Action<InternalError> onError)
        {
            var url = RequestUrl("/client/redeem-gift-code");

            HttpRequest.Post(
                new HttpRequestOptions
                {
                    url = url,
                    headers = Headers(),
                    body = new RedeemGiftCode.Request
                    {
                        identityToken = identityToken,
                        giftCode = giftCode,
                        serverId = serverId,
                        roleId = roleId,
                        roleName = roleName
                    },
                    auth = new HttpBasicAuth(gameId, publishableKey),
                },
                httpResp =>
                {
                    if (httpResp.IsSuccess) Log.D(httpResp.ToString());
                    else Log.D(httpResp.ToString());
                    
                    var resp = ComboResponse<RedeemGiftCode.Response>.CreateResponse(httpResp, url);

                    if(resp.IsSuccess)
                    {
                        onResp.Invoke(resp);
                    }
                    else
                    {
                        HandleError("RedeemGiftCode", resp.Error, false);
                        onError(resp.Error);
                    }
                });
        }

        private static string GetCheckAnnouncementsUrl(int? lastTime = null, int? lastLevel = null, string comboId = null, int? level = null)
        {
            Dictionary<string, string> queryPayment = new Dictionary<string, string>();

            if (lastTime.HasValue)
            {
                queryPayment.Add("last_time", lastTime.Value.ToString());
            }

            if (lastLevel.HasValue)
            {
                queryPayment.Add("last_level", lastLevel.Value.ToString());
            }

            if (!string.IsNullOrEmpty(comboId))
            {
                queryPayment.Add("combo_id", comboId);
            }

            if (level.HasValue)
            {
                queryPayment.Add("level", level.Value.ToString());
            }

            return string.Join("&", queryPayment.Select(kvp => $"{kvp.Key}={kvp.Value}"));
        }

        private static ServerCredential.Credential ToServerCredential(Credential credential)
        {
            if (credential.GetType() == typeof(SeayooCredential))
            {
                var castCredential = (SeayooCredential)credential;
                return new ServerCredential.Seayoo
                {
                    userId = castCredential.userId,
                    token = castCredential.token,
                };
            }
            else if (credential.GetType() == typeof(TaptapCredential))
            {
                var castCredential = (TaptapCredential)credential;
                return new ServerCredential.TapTap
                {
                    kid = castCredential.kid,
                    macKey = castCredential.macKey,
                    ageRange = castCredential.ageRange,
                };
            }
            else if (credential.GetType() == typeof(LenovoCredential))
            {
                var castCredential = (LenovoCredential)credential;
                return new ServerCredential.Lenovo
                {
                    st = castCredential.st,
                    isAuthened = castCredential.isAuthened,
                    age = castCredential.age
                };
            }
            return null;
        }

        private static string RequestUrl(string path)
        {
            return endpoint + version + path;
        }

        private static void HandleError(string operationName, InternalError error, bool isShowToast = true)
        {
            var serverError = ErrorExtensions.Convert<ServerError>(error);
            var networkError = ErrorExtensions.Convert<NetworkError>(error);
            if(networkError != null)
            {
                if(isShowToast)
                {
                    UIController.Instance.ShowToast(I18n.T("network_error"));
                }
                Log.I($"Failed to {operationName}, status_code = NetworkError, error = {error.Message}");
            }
            if(serverError != null)
            {
                if(isShowToast)
                {
                    UIController.Instance.ShowToast(serverError.ErrorMsg);
                }
                Log.I($"Failed to {operationName}, status_code = ServerError, error = {serverError.Error}, error_message = {serverError.ErrorMsg}");

            }
        }

        private static Dictionary<string, string> Headers()
        {
            return new Dictionary<string, string>()
            {
                {"Accept-Language", I18n.GetLanguageCode()},
                {"x-timestamp-unix", Utils.UnixTimeStamp()},
                {"x-game-distro", distro},
                {"x-game-platform", "windows"},
                {"x-sdk-version", $"combo-sdk-windows/{Seayoo.ComboSDK.Windows.Version.Version.SDKVersion} combo-sdk-unity/{unitySdkVersion} (unity/{unityVersion})"},
                {"x-bundle-id", string.IsNullOrEmpty(Application.identifier) ? gameId : Application.identifier },
                {"x-bundle-version", Application.version},
                {"x-device-id", _Core.Device.UUID},
                {"x-device-name", HttpUtils.UrlEncode(_Core.Device.DeviceName)},
                {"x-device-model", HttpUtils.UrlEncode(_Core.Device.Model)},
                {"x-os-type", _Core.Device.OSType},
                {"x-os-version", _Core.Device.OSVersion}
            };
        }
    }
}
