using Senparc.CO2NET.Extensions;
using Senparc.CO2NET.HttpUtility;
using Senparc.Weixin;
using Senparc.Weixin.CommonAPIs;
using Senparc.Weixin.Entities;
using Senparc.Weixin.Entities.TemplateMessage;
using Senparc.Weixin.Exceptions;
using Senparc.Weixin.MP;
using Senparc.Weixin.MP.Containers;
using Senparc.Weixin.MP.Entities;
using System;
using System.Threading.Tasks;
using Znyc.Admin.Commons.Cache;

namespace Wx
{
    /// <summary>
    ///     通用接口
    ///     通用接口用于和微信服务器通讯,一般不涉及自有网站服务器的通讯
    /// </summary>
    public static class CommonHelper
    {
        #region 同步方法

        /// <summary>
        ///     获取凭证接口
        /// </summary>
        /// <param name="grant_type">获取access_token填写client_credential</param>
        /// <param name="appid">第三方用户唯一凭证</param>
        /// <param name="secret">第三方用户唯一凭证密钥,既appsecret</param>
        /// <returns></returns>
        public static AccessTokenResult GetToken(string appid, string secret, string grant_type = "client_credential")
        {
            //注意:此方法不能再使用ApiHandlerWapper.TryCommonApi(),否则会循环
            string url = string.Format(Config.ApiMpHost + "/cgi-bin/token?grant_type={0}&appid={1}&secret={2}",
                grant_type.AsUrlData(), appid.AsUrlData(), secret.AsUrlData());

            AccessTokenResult result = Get.GetJson<AccessTokenResult>(CommonDI.CommonSP, url); //此处为最原始接口,不再使用重试获取的封装

            if (Config.ThrownWhenJsonResultFaild && result.errcode != ReturnCode.请求成功)
            {
                throw new ErrorJsonResultException(
                    string.Format("微信请求发生错误(CommonApi.GetToken)!错误代码:{0},说明:{1}",
                        (int)result.errcode, result.errmsg), null, result);
            }

            return result;
        }

        /// <summary>
        ///     用户信息接口
        /// </summary>
        /// <param name="accessTokenOrAppId">AccessToken或AppId(推荐使用AppId,需要先注册)</param>
        /// <param name="openId"></param>
        /// <returns></returns>
        public static WeixinUserInfoResult GetUserInfo(string accessTokenOrAppId, string openId)
        {
            return ApiHandlerWapper.TryCommonApi(accessToken =>
            {
                string url = string.Format(Config.ApiMpHost + "/cgi-bin/user/info?access_token={0}&openid={1}",
                    accessToken.AsUrlData(), openId.AsUrlData());
                WeixinUserInfoResult result = CommonJsonSend.Send<WeixinUserInfoResult>(null, url, null, CommonJsonSendType.GET);
                return result;
            }, accessTokenOrAppId);
        }

        /// <summary>
        ///     获取调用微信JS接口的临时票据
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="secret"></param>
        /// <param name="type">默认为jsapi,当作为卡券接口使用时,应当为wx_card</param>
        /// <returns></returns>
        public static JsApiTicketResult GetTicket(string appId, string secret, string type = "jsapi")
        {
            string accessToken = AccessTokenContainer.TryGetAccessToken(appId, secret);
            return GetTicketByAccessToken(accessToken, type);
        }

        /// <summary>
        ///     获取调用微信JS接口的临时票据
        /// </summary>
        /// <param name="accessTokenOrAppId">AccessToken或AppId(推荐使用AppId,需要先注册)</param>
        /// <param name="type">默认为jsapi,当作为卡券接口使用时,应当为wx_card</param>
        /// <returns></returns>
        public static JsApiTicketResult GetTicketByAccessToken(string accessTokenOrAppId, string type = "jsapi")
        {
            return ApiHandlerWapper.TryCommonApi(accessToken =>
            {
                string url = string.Format(Config.ApiMpHost + "/cgi-bin/ticket/getticket?access_token={0}&type={1}",
                    accessToken.AsUrlData(), type.AsUrlData());

                JsApiTicketResult result = CommonJsonSend.Send<JsApiTicketResult>(null, url, null, CommonJsonSendType.GET);
                return result;
            }, accessTokenOrAppId);
        }

        /// <summary>
        ///     获取微信服务器的ip段
        /// </summary>
        /// <param name="accessTokenOrAppId">AccessToken或AppId(推荐使用AppId,需要先注册)</param>
        /// <returns></returns>
        public static GetCallBackIpResult GetCallBackIp(string accessTokenOrAppId)
        {
            return ApiHandlerWapper.TryCommonApi(accessToken =>
            {
                string url = string.Format(Config.ApiMpHost + "/cgi-bin/getcallbackip?access_token={0}",
                    accessToken.AsUrlData());

                return CommonJsonSend.Send<GetCallBackIpResult>(null, url, null, CommonJsonSendType.GET);
            }, accessTokenOrAppId);
        }

        /// <summary>
        ///     公众号调用或第三方平台帮公众号调用对公众号的所有api调用(包括第三方帮其调用)次数进行清零
        /// </summary>
        /// <param name="accessTokenOrAppId">AccessToken或AppId(推荐使用AppId,需要先注册)</param>
        /// <param name="appId"></param>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        public static WxJsonResult Clear_quota(string accessTokenOrAppId, string appId, int timeOut = Config.TIME_OUT)
        {
            return ApiHandlerWapper.TryCommonApi(accessToken =>
            {
                string urlFormat = string.Format(Config.ApiMpHost + "/cgi-bin/clear_quota?access_token={0}",
                    accessToken.AsUrlData());
                var data = new
                {
                    appid = appId
                };

                return CommonJsonSend.Send<WxJsonResult>(null, urlFormat, data, timeOut: timeOut);
            }, accessTokenOrAppId);
        }

        #endregion 同步方法

        #region 异步方法

        /// <summary>
        ///     【异步方法】获取凭证接口
        /// </summary>
        /// <param name="grant_type">获取access_token填写client_credential</param>
        /// <param name="appid">第三方用户唯一凭证</param>
        /// <param name="secret">第三方用户唯一凭证密钥,既appsecret</param>
        /// <returns></returns>
        public static async Task<AccessTokenResult> GetTokenAsync(string appid, string secret,
            string grant_type = "client_credential")
        {
            CacheHelper cacheHelper = new CacheHelper();
            AccessTokenResult result;
            result = cacheHelper.Get<AccessTokenResult>(appid);
            if (result == null)
            {
                string url = string.Format(Config.ApiMpHost + "/cgi-bin/token?grant_type={0}&appid={1}&secret={2}",
                    grant_type.AsUrlData(), appid.AsUrlData(), secret.AsUrlData());

                result = await Get.GetJsonAsync<AccessTokenResult>(CommonDI.CommonSP, url); //此处为最原始接口,不再使用重试获取的封装

                if (Config.ThrownWhenJsonResultFaild && result.errcode != ReturnCode.请求成功)
                {
                    throw new ErrorJsonResultException(
                        string.Format("微信请求发生错误(CommonApi.GetToken)!错误代码:{0},说明:{1}",
                            (int)result.errcode, result.errmsg), null, result);
                }

                TimeSpan expiresSliding = DateTime.Now.AddSeconds(7200) - DateTime.Now;
                cacheHelper.Add(appid, result, expiresSliding);
            }

            return result;
        }

        /// <summary>
        ///     【异步方法】用户信息接口
        /// </summary>
        /// <param name="accessTokenOrAppId">AccessToken或AppId(推荐使用AppId,需要先注册)</param>
        /// <param name="openId"></param>
        /// <returns></returns>
        public static async Task<WeixinUserInfoResult> GetUserInfoAsync(string accessTokenOrAppId, string openId)
        {
            return await ApiHandlerWapper.TryCommonApiAsync(async accessToken =>
            {
                string url = string.Format(Config.ApiMpHost + "/cgi-bin/user/info?access_token={0}&openid={1}",
                    accessToken.AsUrlData(), openId.AsUrlData());
                Task<WeixinUserInfoResult> result = CommonJsonSend.SendAsync<WeixinUserInfoResult>(null, url, null, CommonJsonSendType.GET);
                return await result.ConfigureAwait(false);
            }, accessTokenOrAppId).ConfigureAwait(false);
        }

        /// <summary>
        ///     【异步方法】获取调用微信JS接口的临时票据
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="secret"></param>
        /// <param name="type">默认为jsapi,当作为卡券接口使用时,应当为wx_card</param>
        /// <returns></returns>
        public static async Task<JsApiTicketResult> GetTicketAsync(string appId, string secret, string type = "jsapi")
        {
            string accessToken = await AccessTokenContainer.TryGetAccessTokenAsync(appId, secret).ConfigureAwait(false);
            return GetTicketByAccessToken(accessToken, type);
        }

        /// <summary>
        ///     【异步方法】获取调用微信JS接口的临时票据
        /// </summary>
        /// <param name="accessTokenOrAppId">AccessToken或AppId(推荐使用AppId,需要先注册)</param>
        /// <param name="type">默认为jsapi,当作为卡券接口使用时,应当为wx_card</param>
        /// <returns></returns>
        public static async Task<JsApiTicketResult> GetTicketByAccessTokenAsync(string accessTokenOrAppId,
            string type = "jsapi")
        {
            return await ApiHandlerWapper.TryCommonApiAsync(async accessToken =>
            {
                string url = string.Format(Config.ApiMpHost + "/cgi-bin/ticket/getticket?access_token={0}&type={1}",
                    accessToken.AsUrlData(), type.AsUrlData());

                Task<JsApiTicketResult> result = CommonJsonSend.SendAsync<JsApiTicketResult>(null, url, null, CommonJsonSendType.GET);
                return await result.ConfigureAwait(false);
            }, accessTokenOrAppId).ConfigureAwait(false);
        }

        /// <summary>
        ///     【异步方法】获取微信服务器的ip段
        /// </summary>
        /// <param name="accessTokenOrAppId">AccessToken或AppId(推荐使用AppId,需要先注册)</param>
        /// <returns></returns>
        public static async Task<GetCallBackIpResult> GetCallBackIpAsync(string accessTokenOrAppId)
        {
            return await ApiHandlerWapper.TryCommonApiAsync(async accessToken =>
            {
                string url = string.Format(Config.ApiMpHost + "/cgi-bin/getcallbackip?access_token={0}",
                    accessToken.AsUrlData());

                return await CommonJsonSend.SendAsync<GetCallBackIpResult>(null, url, null, CommonJsonSendType.GET)
                    .ConfigureAwait(false);
            }, accessTokenOrAppId).ConfigureAwait(false);
        }

        /// <summary>
        ///     【异步方法】公众号调用或第三方平台帮公众号调用对公众号的所有api调用(包括第三方帮其调用)次数进行清零
        /// </summary>
        /// <param name="accessTokenOrAppId">AccessToken或AppId(推荐使用AppId,需要先注册)</param>
        /// <param name="appId"></param>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        public static async Task<WxJsonResult> Clear_quotaAsync(string accessTokenOrAppId, string appId,
            int timeOut = Config.TIME_OUT)
        {
            return await ApiHandlerWapper.TryCommonApiAsync(async accessToken =>
            {
                string urlFormat = string.Format(Config.ApiMpHost + "/cgi-bin/clear_quota?access_token={0}",
                    accessToken.AsUrlData());
                var data = new
                {
                    appid = appId
                };

                return await CommonJsonSend.SendAsync<WxJsonResult>(null, urlFormat, data, timeOut: timeOut)
                    .ConfigureAwait(false);
            }, accessTokenOrAppId).ConfigureAwait(false);
        }

        /// <summary>
        /// 发送公司审核通知
        /// </summary>
        /// <param name="thing13"></param>
        /// <param name="phrase2"></param>
        /// <param name="thing3"></param>
        /// <param name="toUser"></param>
        /// <returns></returns>
        public static async Task SendCompanyAuditAsync(string thing13,string phrase2, string thing3, string toUser)
        {
            TemplateMessageData templateMessageData = new TemplateMessageData
            {
                ["thing13"] = new TemplateMessageDataValue(thing13),
                ["date4"] = new TemplateMessageDataValue(SystemTime.Now.ToString("yyyy年MM月dd日 HH:mm")),
                ["phrase2"] = new TemplateMessageDataValue(phrase2),
                ["thing3"] = new TemplateMessageDataValue(thing3)
            };
            await MessageHelper.SendSubscribeAsync(templateMessageData,
                "2t_x25fDv2ekqJJPfCiLj-_Sc94bT9J9XzaR3-6FlCM", toUser, "pages/login/login");
        }

        /// <summary>
        ///     实名认证失败发送通知
        /// </summary>
        /// <returns></returns>
        public static async Task SendAuditFailAsync(string toUser)
        {
            string thing1 = "实名认证信息";
            TemplateMessageData templateMessageData = new TemplateMessageData
            {
                ["thing1"] = new TemplateMessageDataValue(thing1),
                ["time2"] = new TemplateMessageDataValue(SystemTime.Now.ToString("yyyy年MM月dd日 HH:mm")),
                ["phrase3"] = new TemplateMessageDataValue("审核失败"),
                ["thing4"] = new TemplateMessageDataValue("")
            };
            await MessageHelper.SendSubscribeAsync(templateMessageData,
                "dm1yuO5nVqiRQyMWcS4VZOjOZY5rDCjhiqiUcueyMJc", toUser, "/page/index/index");
        }

        /// <summary>
        ///     实名认证审核成功
        /// </summary>
        /// <param name="toUser"></param>
        /// <returns></returns>
        public static async Task SendAuditSuccessAsync(string toUser)
        {
            string thing1 = "实名认证信息";
            //toUser = "oX5AI5rhIvgBDtuy3ytvkSao7eG4";
            TemplateMessageData templateMessageData = new TemplateMessageData
            {
                ["thing1"] = new TemplateMessageDataValue(thing1),
                ["time2"] = new TemplateMessageDataValue(SystemTime.Now.ToString("yyyy年MM月dd日 HH:mm")),
                ["phrase3"] = new TemplateMessageDataValue("审核通过"),
                ["thing4"] = new TemplateMessageDataValue("满足通过条件")
            };
            await MessageHelper.SendSubscribeAsync(templateMessageData,
                "dm1yuO5nVqiRQyMWcS4VZOjOZY5rDCjhiqiUcueyMJc", toUser, "/page/index/index");
        }

        #endregion 异步方法
    }
}