using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using Autofac;
using EMIS.Utility;
using EMIS.ViewModel;
using EMIS.Utility.OnlinePay.WeChat;
using Senparc.Weixin;
using Senparc.Weixin.MP;
using Senparc.Weixin.MP.Entities.Request;
using Senparc.Weixin.MP.MvcExtension;
using Senparc.Weixin.TenPay;
using Senparc.Weixin.TenPay.V3;
using Senparc.Weixin.MP.Containers;
using Senparc.Weixin.MP.AdvancedAPIs;
using System.Configuration;
using Senparc.Weixin.MP.Helpers;
using System.Xml.Linq;
using EMIS.ViewModel.WechatModel;
using Bowin.Common.Log;

namespace EMIS.Utility.OnlinePay
{
    public class WechatHelper
    {
        private static readonly string Token = Config.SenparcWeixinSetting.Token == null ? ConfigurationManager.AppSettings["WeixinToken"] : Config.SenparcWeixinSetting.Token;//与微信公众账号后台的Token设置保持一致,区分大小写。
        private static readonly string EncodingAESKey = Config.SenparcWeixinSetting.EncodingAESKey == null ? ConfigurationManager.AppSettings["WeixinEncodingAESKey"] : Config.SenparcWeixinSetting.EncodingAESKey;//与微信公众账号后台的EncodingAESKey设置保持一致,区分大小写。
        private static readonly string AppId = Config.SenparcWeixinSetting.WeixinAppId == null ? ConfigurationManager.AppSettings["WeixinAppId"] : Config.SenparcWeixinSetting.WeixinAppId;//与微信公众账号后台的AppId设

        private static readonly string TenPayV3_MchId = Config.SenparcWeixinSetting.TenPayV3_MchId == null ? ConfigurationManager.AppSettings["TenPayV3_MchId"] : Config.SenparcWeixinSetting.TenPayV3_MchId;
        private static readonly string TenPayV3_Key = Config.SenparcWeixinSetting.TenPayV3_Key == null ? ConfigurationManager.AppSettings["TenPayV3_Key"] : Config.SenparcWeixinSetting.TenPayV3_Key;
        private static readonly string TenPayV3_AppId = Config.SenparcWeixinSetting.TenPayV3_AppId == null ? ConfigurationManager.AppSettings["TenPayV3_AppId"] : Config.SenparcWeixinSetting.TenPayV3_AppId;
        private static readonly string TenPayV3_AppSecret = Config.SenparcWeixinSetting.TenPayV3_AppSecret == null ? ConfigurationManager.AppSettings["TenPayV3_AppSecret"] : Config.SenparcWeixinSetting.TenPayV3_AppSecret;
        private static readonly string TenPayV3_TenpayNotify = Config.SenparcWeixinSetting.TenPayV3_TenpayNotify == null ? ConfigurationManager.AppSettings["TenPayV3_TenpayNotify"] : Config.SenparcWeixinSetting.TenPayV3_TenpayNotify;
        private static readonly string TenPayV3_RefundNotify = ConfigurationManager.AppSettings["TenPayV3_RefundNotify"];

        #region 公众号基础处理
        public static ActionResult EventHandler(PostModel postModel, string echostr)
        {
            if (CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token))
            {
                return new ContentResult() { Content = echostr }; //返回随机字符串则表示验证通过
            }
            else
            {
                return new ContentResult()
                {
                    Content = "failed:" + postModel.Signature + "," + Senparc.Weixin.MP.CheckSignature.GetSignature(postModel.Timestamp, postModel.Nonce, Token) + "。" +
                    "如果你在浏览器中看到这句话,说明此地址可以被作为微信公众账号后台的Url,请注意保持Token一致。"
                };
            }
        }

        public static ActionResult PostEventHandler(PostModel postModel)
        {
            if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token))
            {
                return new ContentResult() { Content = "参数错误!" };
            }

            postModel.Token = Token;//根据自己后台的设置保持一致
            postModel.EncodingAESKey = EncodingAESKey;//根据自己后台的设置保持一致
            postModel.AppId = AppId;//根据自己后台的设置保持一致

            //自定义MessageHandler,对微信请求的详细判断操作都在这里面。
            var messageHandler = new CustomMessageHandler(HttpContext.Current.Request.InputStream, postModel);//接收消息

            messageHandler.Execute();//执行微信处理过程

            return new FixWeixinBugWeixinResult(messageHandler);//返回结果
        }

        public static void SendAnnouncement(string title, string content, IList<string> openIDList, string examinationProjectName = null, DateTime? startTime = null)
        {
            if (openIDList.Count > 0)
            {
                var templateID = ConfigurationManager.AppSettings["WxMp_NoticeTemplateID"];
                var result = AccessTokenContainer.TryGetAccessToken(Config.SenparcWeixinSetting.WeixinAppId, Config.SenparcWeixinSetting.WeixinAppSecret);

                foreach (var openID in openIDList)
                {
                    if (openID != null && openID != "null")
                    {
                        TemplateApi.SendTemplateMessage(result, openID, templateID, null, new
                        {
                            first = new WxTemplateMessageData
                            {
                                value = (examinationProjectName != null ? examinationProjectName + "项目" : "") + "通知",
                                color = "#FF0000"
                            },
                            remark = new WxTemplateMessageData
                            {
                                value = HttpUtility.HtmlDecode(content)
                            },
                            keyword1 = new WxTemplateMessageData
                            {
                                value = "广东岭南职业技术学院"
                            },
                            keyword2 = new WxTemplateMessageData
                            {
                                value = "鉴定中心"
                                //startTime.HasValue ? startTime.Value.ToString("yyyy年MM月dd日 HH时mm分") : ""
                            },
                            keyword3 = new WxTemplateMessageData
                            {
                                value = startTime.HasValue ? startTime.Value.ToString("yyyy年MM月dd日 HH时mm分") : ""
                            },
                            keyword4 = new WxTemplateMessageData
                            {
                                value = title
                            },
                        });
                    }
                }
            }
        }

        public static void SendAnnouncementGDCX(string title, string content,string userName, IList<string> openIDList, string examinationProjectName = null, DateTime? startTime = null)
        {
            if (openIDList.Count > 0)
            {
                var templateID = ConfigurationManager.AppSettings["WxMp_NoticeTemplateID"];
                var result = AccessTokenContainer.TryGetAccessToken(Config.SenparcWeixinSetting.WeixinAppId, Config.SenparcWeixinSetting.WeixinAppSecret);

                foreach (var openID in openIDList)
                {
                    if (openID != null && openID != "null")
                    {
                        TemplateApi.SendTemplateMessage(result, openID, templateID, null, new
                        {
                            first = new WxTemplateMessageData
                            {
                                value = title,
                                color = "#FF0000"
                            },
                            
                            keyword1 = new WxTemplateMessageData
                            {
                                value = "广东创新科技职业学院"
                            },
                            keyword2 = new WxTemplateMessageData
                            {
                                value = "继续教育学院"
                                //startTime.HasValue ? startTime.Value.ToString("yyyy年MM月dd日 HH时mm分") : ""
                            },
                            keyword3 = new WxTemplateMessageData
                            {
                                value = userName
                            },
                            keyword4 = new WxTemplateMessageData
                            {
                                value = startTime.HasValue ? startTime.Value.ToString("yyyy年MM月dd日 HH时mm分") : ""
                            },
                            remark = new WxTemplateMessageData
                            {
                                value = HttpUtility.HtmlDecode(content)
                            },
                        });
                    }
                }
            }
        }

        public static JsSdkUiPackage GetJSConfigData(string url)
        {
            try
            {
                return JSSDKHelper.GetJsSdkUiPackage(TenPayV3_AppId, TenPayV3_AppSecret, url);
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog(LogType.ServiceLog, ex.Message);
                throw;
            }
        }

        public static string GetBaseAuthorizeUrl(string url,string state = null)
        {
            var stateStr = "";
            if (state != null)
            {
                stateStr = state;
            }
            return OAuthApi.GetAuthorizeUrl(TenPayV3_AppId, url, stateStr, OAuthScope.snsapi_base) + "&v=" + DateTime.Now.ToFileTime().ToString();
        }
        public static string GetUserInfoAuthorizeUrl(string url,string state = null)
        {
            var stateStr = "";
            if (state != null)
            {
                stateStr = state;
            }
            return OAuthApi.GetAuthorizeUrl(TenPayV3_AppId, url, stateStr, OAuthScope.snsapi_userinfo) + "&v=" + DateTime.Now.ToFileTime().ToString();
        }

        public static string GetOpenID(string code)
        {
            return OAuthApi.GetAccessToken(TenPayV3_AppId, TenPayV3_AppSecret, code).openid;
        }

        public static string DownloadMedia(string mediaID)
        {
            var stream = new MemoryStream();
            try
            {
                
                MediaApi.Get(TenPayV3_AppId, mediaID, stream);
                return FileUploadHelper.UploadFile(stream, ".png");
            }
            catch (Exception)
            {
                throw;
            }

            
        }
        #endregion

        #region 支付
        public static FileContentResult GetPayQRCode(string productId, decimal fee, string feeTypeName, out string payUrl)
        {
            string timestamp = TenPayV3Util.GetTimestamp();
            string nonceString = TenPayV3Util.GetNoncestr();
            int intFee = Convert.ToInt32(Math.Round(fee * 100, 0));

            TenPayV3UnifiedorderRequestData orderData = new TenPayV3UnifiedorderRequestData(TenPayV3_AppId, TenPayV3_MchId, feeTypeName, productId, intFee, HttpContext.Current.Request.UserHostAddress,
                TenPayV3_TenpayNotify, Senparc.Weixin.TenPay.TenPayV3Type.NATIVE, null, TenPayV3_Key, nonceString);
            //TenPayV3UnifiedorderRequestData orderData = new TenPayV3UnifiedorderRequestData(TenPayV3_AppId, TenPayV3_MchId, feeTypeName, productId, 1, HttpContext.Current.Request.UserHostAddress,
            //    TenPayV3_TenpayNotify, Senparc.Weixin.TenPay.TenPayV3Type.NATIVE, null, TenPayV3_Key, nonceString);

            UnifiedorderResult result = TenPayV3.Unifiedorder(orderData);
            if (result.return_code == "SUCCESS")
            {
                var memoryStream = new MemoryStream();
                var qrcodeImage = QRCodeHelper.GenerateQRCode(result.code_url);
                qrcodeImage.Save(memoryStream, ImageFormat.Png);

                payUrl = result.code_url;

                return new FileContentResult(memoryStream.GetBuffer(), "image/png");
            }
            else
            {
                throw new Exception("生成二维码失败:调用统一下单接口失败。");
            }
        }

        public static JSPayView JsPay(string productId, decimal fee, string feeTypeName, string openId)
        {
            var result = new JSPayView();
            result.AppID = TenPayV3_AppId;
            result.TimeStamp = TenPayV3Util.GetTimestamp();
            result.NonceStr = TenPayV3Util.GetNoncestr();
            int intFee = Convert.ToInt32(Math.Round(fee * 100, 0));

            TenPayV3UnifiedorderRequestData orderData = new TenPayV3UnifiedorderRequestData(TenPayV3_AppId, TenPayV3_MchId, feeTypeName, productId, intFee, HttpContext.Current.Request.UserHostAddress,
                TenPayV3_TenpayNotify, Senparc.Weixin.TenPay.TenPayV3Type.JSAPI, openId, TenPayV3_Key, result.NonceStr);

            UnifiedorderResult orderResult = TenPayV3.Unifiedorder(orderData);
            if (orderResult.return_code == "SUCCESS")
            {
                result.Package = "prepay_id=" + orderResult.prepay_id;
                result.PaySign = TenPayV3.GetJsPaySign(TenPayV3_AppId, result.TimeStamp, result.NonceStr, result.Package, TenPayV3_Key);
            }
            else
            {
                throw new Exception("支付失败:调用统一下单接口失败。" + orderResult.return_msg);
            }

            return result;
        }

        public static bool CloseOrder(string productId)
        {
            bool isOK = true;
            string timestamp = TenPayV3Util.GetTimestamp();
            string nonceString = TenPayV3Util.GetNoncestr();

            TenPayV3CloseOrderRequestData orderData = new TenPayV3CloseOrderRequestData(TenPayV3_AppId, TenPayV3_MchId, productId, TenPayV3_Key, nonceString);

            CloseOrderResult result = TenPayV3.CloseOrder(orderData);
            if (result.return_code != "SUCCESS")
            {
                isOK = false;
                //throw new Exception("关闭重复订单失败:" + result.return_msg);
            }
            else if (result.result_code != "SUCCESS")
            {
                isOK = false;
                //throw new Exception("关闭重复订单失败:" + result.result_msg);
            }
            return isOK;
        }

        /// <summary>
        /// 接收微信支付返回信息
        /// </summary>
        /// <returns></returns>
        public static PayNotifyView PayNotify()
        {
            ResponseHandler resHandler = new ResponseHandler(null);
            resHandler.Init();
            resHandler.SetKey(TenPayV3_Key);

            var response = HttpContext.Current.Response;

            response.Clear();
            //判断签名
            if (resHandler.IsTenpaySign())
            {
                PayNotifyView payNotifyView = new PayNotifyView();
                //商户在收到后台通知后根据通知ID向财付通发起验证确认,采用后台系统调用交互模式
                payNotifyView.NotifyID = resHandler.GetParameter("notify_id");
                //取结果参数做业务处理
                payNotifyView.OrderID = resHandler.GetParameter("out_trade_no");
                //财付通订单号
                payNotifyView.WechatOrderID = resHandler.GetParameter("transaction_id");
                //金额,以分为单位
                payNotifyView.TotalFee = resHandler.GetParameter("total_fee");
                //如果有使用折扣券,discount有值,total_fee+discount=原请求的total_fee
                payNotifyView.Discount = resHandler.GetParameter("discount");
                //支付结果
                payNotifyView.ResultCode = resHandler.GetParameter("result_code");
                //范例上有个trade_state参数,估计是旧版接口……
                payNotifyView.TradeState = resHandler.GetParameter("trade_state");

                //即时到账
                if ("SUCCESS".Equals(payNotifyView.ResultCode))
                {
                    return payNotifyView;
                }
                else
                {
                    string exceptionMessage = "支付失败";
                    response.Write("<xml><return_code>FAIL</return_code><return_msg>exceptionMessage</return_msg></xml>");
                    response.End();
                    throw new Exception(exceptionMessage);
                }
            }
            else
            {//md5签名失败
                string exceptionMessage = "MD5签名失败:" + resHandler.GetDebugInfo();
                response.Write("<xml><return_code>FAIL</return_code><return_msg>exceptionMessage</return_msg></xml>");
                response.End();
                throw new Exception(exceptionMessage);
            }
        }

        public static ActionResult PayNotifySuccess
        {
            get
            {
                return new ContentResult() { Content = "<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>", ContentEncoding = Encoding.UTF8 };
            }
        }

        public static ActionResult PayNotifyFail(string errorMessage)
        {
            return new ContentResult() { Content = "<xml><return_code>FAIL</return_code><return_msg>" + errorMessage + "</return_msg></xml>", ContentEncoding = Encoding.UTF8 };
        }

        public static OrderQueryResult OrderQuery(string productId)
        {
            string nonceStr = TenPayV3Util.GetNoncestr();
            RequestHandler packageReqHandler = new RequestHandler(null);

            //设置package订单参数
            var datainfo = new TenPayV3OrderQueryRequestData(TenPayV3_AppId, TenPayV3_MchId, "", nonceStr, productId, TenPayV3_Key);

            var result = TenPayV3.OrderQuery(datainfo);

            return result;
        }
        #endregion

        #region 退款
        public static void Refund(string orderID, decimal totalFee, decimal fee)
        {
            //totalFee = (decimal)0.01;
            //fee = (decimal)0.01;
            //orderID = "20190801000019";
            if (fee > totalFee)
            {
                throw new Exception("退款金额不能大于总金额。");
            }
            string nonceStr = TenPayV3Util.GetNoncestr();

            string outTradeNo = orderID;
            string outRefundNo = "RN-" + orderID + SystemTime.Now.Ticks;
            int totalFeeInt = Convert.ToInt32(Math.Round(totalFee * 100, 0));
            int refundFeeInt = Convert.ToInt32(Math.Round(fee * 100, 0));
            string opUserId = TenPayV3_MchId;
            var notifyUrl = TenPayV3_RefundNotify;
            var dataInfo = new TenPayV3RefundRequestData(TenPayV3_AppId, TenPayV3_MchId, TenPayV3_Key,
                null, nonceStr, null, outTradeNo, outRefundNo, totalFeeInt, refundFeeInt, opUserId, null, notifyUrl: notifyUrl);
            var cert = @"D:\webs\jdks\wechatpay\cert\apiclient_cert.p12";//根据自己的证书位置修改
            //var cert = @"H:\surfacepro备份\开发项目\教务管理系统\岭南职业技术学院源码\1544542811_20190802_cert\apiclient_cert.p12";
            var password = TenPayV3_MchId;//默认为商户号,建议修改
            var result = TenPayV3.Refund(dataInfo, cert, password);

            if (result.return_code != "SUCCESS")
            {
                throw new Exception(result.return_msg);
            }
            if (result.result_code != "SUCCESS")
            {
                if (result.err_code == "NOTENOUGH")
                {
                    dataInfo = new TenPayV3RefundRequestData(TenPayV3_AppId, TenPayV3_MchId, TenPayV3_Key,
                        null, nonceStr, null, outTradeNo, outRefundNo, totalFeeInt, refundFeeInt, opUserId, "REFUND_SOURCE_RECHARGE_FUNDS", notifyUrl: notifyUrl);
                    result = TenPayV3.Refund(dataInfo, cert, password);
                    if (result.return_code != "SUCCESS")
                    {
                        throw new Exception(result.return_msg);
                    }
                    if (result.result_code != "SUCCESS")
                    {
                        throw new Exception("(" + result.err_code + ")" + result.err_code_des);
                    }
                }
                else
                {
                    throw new Exception("(" + result.err_code + ")" + result.err_code_des);
                }
            }
        }

        /// <summary>
        /// 退款通知处理
        /// </summary>
        /// <returns></returns>
        public static RefundNodifyView RefundNotify()
        {
            ResponseHandler resHandler = new ResponseHandler(null);

            string return_code = resHandler.GetParameter("return_code");
            string return_msg = resHandler.GetParameter("return_msg");

            if (return_code == "SUCCESS")
            {
                string appId = resHandler.GetParameter("appid");
                string mch_id = resHandler.GetParameter("mch_id");
                string nonce_str = resHandler.GetParameter("nonce_str");
                string req_info = resHandler.GetParameter("req_info");

                var decodeReqInfo = TenPayV3Util.DecodeRefundReqInfo(req_info, TenPayV3_Key);
                var decodeDoc = XDocument.Parse(decodeReqInfo);

                //获取接口中需要用到的信息
                var refundNodifyView = new RefundNodifyView();
                refundNodifyView.WechatOrderID = decodeDoc.Root.Element("transaction_id").Value;
                refundNodifyView.OrderID = decodeDoc.Root.Element("out_trade_no").Value;
                refundNodifyView.WechatRefundOrderID = decodeDoc.Root.Element("refund_id").Value;
                refundNodifyView.RefundOrderID = decodeDoc.Root.Element("out_refund_no").Value;
                refundNodifyView.Fee = decimal.Parse(decodeDoc.Root.Element("refund_fee").Value) / 100;

                return refundNodifyView;
            }
            else
            {
                throw new Exception(return_msg);
            }
        }
        #endregion

        public static PayNotifyView SearchWechatOrderStatus()
        {
            ResponseHandler resHandler = new ResponseHandler(null);
            resHandler.Init();
            resHandler.SetKey(TenPayV3_Key);

            var response = HttpContext.Current.Response;

            response.Clear();
            //判断签名
            if (resHandler.IsTenpaySign())
            {
                PayNotifyView payNotifyView = new PayNotifyView();
                //商户在收到后台通知后根据通知ID向财付通发起验证确认,采用后台系统调用交互模式
                payNotifyView.NotifyID = resHandler.GetParameter("notify_id");
                //取结果参数做业务处理
                payNotifyView.OrderID = resHandler.GetParameter("out_trade_no");
                //财付通订单号
                payNotifyView.WechatOrderID = resHandler.GetParameter("transaction_id");
                //金额,以分为单位
                payNotifyView.TotalFee = resHandler.GetParameter("total_fee");
                //如果有使用折扣券,discount有值,total_fee+discount=原请求的total_fee
                payNotifyView.Discount = resHandler.GetParameter("discount");
                //支付结果
                payNotifyView.ResultCode = resHandler.GetParameter("result_code");
                //范例上有个trade_state参数,估计是旧版接口……

                //即时到账
                if ("SUCCESS".Equals(payNotifyView.ResultCode))
                {
                    return payNotifyView;
                }
                else
                {
                    string exceptionMessage = "支付失败";
                    response.Write("<xml><return_code>FAIL</return_code><return_msg>exceptionMessage</return_msg></xml>");
                    response.End();
                    throw new Exception(exceptionMessage);
                }
            }
            else
            {//md5签名失败
                string exceptionMessage = "MD5签名失败:" + resHandler.GetDebugInfo();
                response.Write("<xml><return_code>FAIL</return_code><return_msg>exceptionMessage</return_msg></xml>");
                response.End();
                throw new Exception(exceptionMessage);
            }
        }
    }
}