using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Edu.Common;
using Edu.Common.API;
using Edu.Common.Plugin;
using Edu.Common.WeChatPayAPIv3;
using Edu.Common.WeChatPayAPIv3.Model;
using Edu.Common.WeChatPayAPIv3.Model.GenerateOrder;
using Edu.Common.WeChatPayAPIv3.Model.QueryOrder;
using Edu.Common.WeChatPayAPIv3.Model.QueryRefunds;
using Edu.Common.WeChatPayAPIv3.Model.Refunds;
using Edu.Common.WeChatPayAPIv3.Model.RefundsCallback;
using Edu.Common.WeChatPayAPIv3.Model.WxPayCallback;
using Edu.Model.Entity.Finance;
using Edu.Module.Course;
using Edu.Module.User;
using Edu.WebApi.Filter;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;

namespace Edu.WebApi.Controllers.WeChatPay
{
    [Route("api/[controller]/[action]")]
    [ApiExceptionFilter]
    [ApiController]
    [EnableCors("AllowCors")]
    public class WeChatPayController : BaseController
    {

        /// <summary>
        /// 订单处理类对象
        /// </summary>
        private readonly EducationContractModule educationContractModule = AOP.AOPHelper.CreateAOPObject<EducationContractModule>();
        /// <summary>
        /// 订单处理类对象
        /// </summary>
        private readonly EmployeeModule employeeModule = AOP.AOPHelper.CreateAOPObject<EmployeeModule>();

        /// <summary>
        /// 订单处理类对象
        /// </summary>
        private readonly OrderModule orderModule = AOP.AOPHelper.CreateAOPObject<OrderModule>();
        private readonly object _lock = new object();
        /// <summary>
        /// 统一下单接口
        /// </summary>
        /// <returns></returns>

        [HttpPost]
        [AllowAnonymous]
        public ApiResult GenerateOrder()
        {
            JObject jobj = JObject.Parse(RequestParm.Msg.ToString());
            int contractId = jobj.GetInt("contractId");
            var orderModle = educationContractModule.GetEducationContractModule(contractId);
            if (orderModle.Status == 4)
            {
                return ApiResult.Failed("合同已取消");
            }
            var orderNumber = $"{DateTime.Now:yyyyMMddHHmmssfff}{contractId}";
            LogHelper.WriteInfo("合同Id:" + contractId + " 商户订单号：" + orderNumber);
            var helper = new WxPayHelper(WxPayConst.appid, WxPayConst.mchid, WxPayConst.serialNo, WxPayConst.privateKey);
            var notify_url = Config.sTenpayNotifyUrl;//ConfigurationManager.AppSettings["notify_url"]; //这个放在配置文件，从配置文件读取比较灵活，或者写到数据库中

            Task<WxPayRespModel> payodel = helper.UnionGenerateOrder(orderModle.CourseName, Convert.ToInt32(orderModle.Money * 100), orderNumber, notify_url, orderModle.OrderId.ToString());

            #region 调用微信支付

            var signModel = WxPayForAppHelper.GetSign(WxPayConst.appid, payodel.Result.code_url, WxPayConst.privateKey);

            #endregion

            return ApiResult.Success(data: signModel);
        }



        /// <summary>
        /// 微信支付成功结果回调接口
        /// </summary>
        /// <returns>退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功，否则会重试。注意：重试过多会导致微信支付端积压过多通知而堵塞，影响其他正常通知。</returns>
        [HttpPost]
        [HttpGet]
        [AllowAnonymous]
        public WxPayCallbackRespModel WxPayCallback(WxPayNotifyModel wxPayNotifyModel)
        {
            var viewModel = new WxPayCallbackRespModel();
            try
            {
                //  var wxPayNotifyModel = Common.Plugin.JsonHelper.DeserializeObject<WxPayNotifyModel>(Request.Body.ToString());  //str.ToObject<WxPayNotifyModel>();
                var resource = wxPayNotifyModel?.resource ?? new WxPayResourceModel();
                var decryptStr = AesGcmHelper.AesGcmDecrypt(resource.associated_data, resource.nonce, resource.ciphertext, WxPayConst.APIV3Key);
                LogHelper.WriteInfo("订单回调信息decryptStr" + decryptStr);
                var payModel = Common.Plugin.JsonHelper.DeserializeObject<WxPayResourceDecryptModel>(decryptStr); //decryptStr.ToObject<WxPayResourceDecryptModel>();

                if (payModel.trade_state == "SUCCESS")
                {
                    viewModel.code = "SUCCESS";
                    viewModel.message = "";
                }
                if (string.IsNullOrEmpty(payModel.out_trade_no))
                {
                    viewModel.code = "FAIL";
                    viewModel.message = "数据解密失败";

                }
                else
                {
                    if (payModel != null && payModel.trade_state == "SUCCESS")//生成财务单据
                    { //然后进行数据库更新处理……等等其他操作
                        UpdateFinance(payModel);//生成财务单据以及生成支付记录
                        viewModel.code = "SUCCESS";
                        viewModel.message = "";
                    }
                }
            }
            catch (Exception ex)
            {
                Common.Plugin.LogHelper.Write(ex, "WxPayCallback");
                viewModel.code = "FAIL";
                viewModel.message = "数据解密失败";
            }
            return viewModel;
        }


        /// <summary>
        /// 生成财务单据以及生成支付记录
        /// </summary>
        /// <param name="payModel"></param>
        /// <returns></returns>

        [AllowAnonymous]
        public void UpdateFinance(WxPayResourceDecryptModel payModel)
        {
            LogHelper.WriteInfo("我是回调" + System.DateTime.Now);
            int contractId = Convert.ToInt32(payModel.out_trade_no[17..]);
            bool isAdd = false;

            if (!Cache.User.UserReidsCache.Exists(Cache.CacheKey.WeChatPay_Callback_Key + payModel.out_trade_no))
            {
                LogHelper.WriteInfo(payModel.out_trade_no + "我是回调：" + System.DateTime.Now);
                Cache.User.UserReidsCache.Set(Cache.CacheKey.WeChatPay_Callback_Key + payModel.out_trade_no, payModel.out_trade_no, 1800);
                isAdd = true;
                Cache.User.UserReidsCache.Set(Cache.CacheKey.WeChatPay_Callback_Key + payModel.out_trade_no, payModel.out_trade_no, 1800);
                var oldOrderRecordModel = educationContractModule.GetOrderRecordList(new RB_Finance_OrderRecord { ContractId = contractId, OutTradeNo = payModel.out_trade_no, Type = 1 }).FirstOrDefault();
                if (oldOrderRecordModel == null || oldOrderRecordModel.ID == 0)
                {
                    isAdd = true;
                }
                if (isAdd)
                {
                    LogHelper.WriteInfo(payModel.out_trade_no + "我要新增财务单据啦...." + System.DateTime.Now);
                    var orderModle = educationContractModule.GetEducationContractModule(contractId);
                    var orderModel = orderModule.GetClassOrderInfoModule(orderModle.OrderId);
                    var financeConfig = educationContractModule.GetFinanceConfigList(new Model.ViewModel.Course.RB_Finance_Config_ViewModel { Group_Id = orderModle.Group_Id, Type = Common.Enum.Course.FinanceConfigTypeEnum.Tuition }).FirstOrDefault();
                    if (financeConfig == null)
                    {
                        financeConfig = new Model.ViewModel.Course.RB_Finance_Config_ViewModel();
                    }
                    var userInfo = employeeModule.GetEmployeeListModule(new Model.ViewModel.User.Employee_ViewModel { Id = orderModel.EnterID, Group_Id = orderModel.Group_Id }).ToList().FirstOrDefault();
                    var OriginalFee = Math.Round((Convert.ToDecimal(Config.SettlementRate) / 100) * (Convert.ToDecimal(payModel.amount.payer_total) / 100), 2, MidpointRounding.AwayFromZero);//手续费
                    RB_Online_Trade_Detail model = new RB_Online_Trade_Detail();
                    RB_Finance_OrderRecord orderRecordModel = new RB_Finance_OrderRecord
                    {
                        ID = 0,
                        Type = 1,
                        FinanceId = 0,
                        OrderId = orderModle.OrderId,
                        ContractId = orderModle.Id,
                        CreateDate = System.DateTime.Now,
                        RB_Group_Id = orderModle.Group_Id,
                        RB_School_Id = orderModle.School_Id,
                        OutTradeNo = payModel.out_trade_no,
                        TransactionId = payModel.transaction_id,
                        TotalPrice = Convert.ToDecimal(payModel.amount.payer_total) / 100,
                        ServiceFee = OriginalFee
                    };
                    #region 财务单据数据组装
                    var detailList = new List<object>
                    {
                        new
                        {
                            CostTypeId = financeConfig?.CostTypeId ?? 533,
                            Number = 1,
                            OriginalMoney = Convert.ToDecimal(payModel.amount.payer_total) / 100,
                            UnitPrice = Convert.ToDecimal(payModel.amount.payer_total) / 100,
                            Remark = orderModle.StudentName + orderModle.CourseName + payModel.out_trade_no
                        }
                    };

                    var financeObj = new
                    {
                        IsPublic = 1,
                        BType = 1,//item.AccountType,
                        AccountId = financeConfig?.ClientID ?? 54,
                        WBMoney = Convert.ToDecimal(payModel.amount.payer_total) / 100,
                        TradeDate = System.DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd"),
                        AccountNumber = System.DateTime.Now.AddDays(-1).ToString("yyyyMMdd"),
                        TemplateId = financeConfig?.TempleteId ?? 139,
                        OrderSource = 17,
                        Remark = System.DateTime.Now.ToString("yyyy年MM月dd日") + "自动生成财务单据",
                        detailList,
                        OriginalFee,
                        CreateBy = userInfo.Id,
                        RemitterName = orderModle.StudentName,
                        RB_Branch_Id = userInfo.School_Id,
                        RB_Group_Id = userInfo.Group_Id,
                        RB_Depart_Id = userInfo.Dept_Id,
                        RB_CreateByName = userInfo.EmployeeName,
                        RB_DepartName = userInfo.DeptName,
                        RB_BranchName = userInfo.SchoolName,
                        RB_GroupName = userInfo.GroupName,
                        FinanceType = 2,
                        orderModle.GuestId,
                        orderModle.OrderId,
                        TCIDList = orderModel.ClassId > 0 ? new List<int>() { orderModel.ClassId } : new List<int>(),
                    };
                    #endregion

                    string sign = EncryptionHelper.AesEncrypt(JsonHelper.Serialize(financeObj), Config.FinanceKey);
                    var resultInfo = new
                    {
                        msg = sign,
                    };
                    string apiResult = Common.Plugin.HttpHelper.HttpPost(Config.ReadConfigKey("IncomeFinanceApi"), JsonHelper.Serialize(resultInfo), "");
                    JObject parmsJob = JObject.Parse(apiResult);
                    string resultCode = parmsJob.GetStringValue("resultCode");
                    int frid = parmsJob.GetInt("data", 0);
                    if (resultCode == "1" && frid > 0)//新增记录
                    {
                        orderRecordModel.FinanceId = frid;
                        model = new RB_Online_Trade_Detail
                        {
                            ID = 0,
                            Type = 1,
                            OrderSource = 18,
                            OrderId = orderModle.OrderId,
                            Pay_Order = "",
                            Trade_Order = payModel.out_trade_no,
                            Third_Order = payModel.transaction_id,
                            Money = Convert.ToDecimal(payModel.amount.payer_total) / 100,
                            Pay_Way = 1,
                            Interface_Company = 0,
                            Version = "",
                            Mch_Id = WxPayConst.mchid,
                            User_Id = orderModle.GuestId.ToString(),
                            Is_follow = 0,
                            AppId = WxPayConst.appid,
                            OpenId = payModel?.payer?.openid ?? "",
                            Currency_Type = payModel.amount.payer_currency,
                            Institution_Type = "",
                            Card_Type = 0,
                            Remarks = "教育订单信息财务单据：" + frid,
                            Pay_Result = 0,
                            Pay_Date = !string.IsNullOrWhiteSpace(payModel?.success_time ?? "") ? DateTime.SpecifyKind(Convert.ToDateTime(payModel.success_time), DateTimeKind.Utc) : Convert.ToDateTime("1990-01-01"),
                            Payer_Id = orderModle.GuestId,
                            Payer_Type = 0,
                            Data_Source = frid,
                            RB_Group_Id = orderModle.Group_Id,
                            RB_Branch_Id = orderModle.School_Id,
                            IsRefund = 0,
                            RefundTrade_Order = "",
                            RefundMoney = 0,
                            RefundStatus = 0,
                            FinanceId = 0,

                        };
                    }
                    bool result = educationContractModule.SetEducationContractFinance(model, orderRecordModel);
                    Cache.User.UserReidsCache.Set(Cache.CacheKey.WeChatPay_Callback_Key + payModel.out_trade_no, payModel.out_trade_no, 1);
                }
            }

            else
            {
                LogHelper.WriteInfo("存在Key" + System.DateTime.Now);
            }

        }

        /// <summary>
        /// 根据商户订单号查询订单支付信息
        /// </summary>
        /// <param name="orderNumber"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [AllowRepeatAttribute]
        public ApiResult QueryOrder()
        {
            JObject jobj = JObject.Parse(RequestParm.Msg.ToString());
            string orderNumber = jobj.GetStringValue("orderNumber");
            var helper = new WxPayHelper(WxPayConst.appid, WxPayConst.mchid, WxPayConst.serialNo, WxPayConst.privateKey);
            var payModel = helper.QueryOrder(orderNumber).Result;

            return ApiResult.Success(data: payModel);
        }

        ///// <summary>
        ///// 根据商户订单号查询订单支付信息
        ///// </summary>
        ///// <param name="orderNumber"></param>
        ///// <returns></returns>
        //[AllowAnonymous]
        //public async Task<WxPayStatusRespModel> QueryOrder()
        //{
        //    JObject jobj = JObject.Parse(RequestParm.Msg.ToString());
        //    string orderNumber = jobj.GetStringValue("orderNumber");
        //    var helper = new WxPayHelper(WxPayConst.appid, WxPayConst.mchid, WxPayConst.serialNo, WxPayConst.privateKey);
        //    var payModel = await helper.QueryOrder(orderNumber);

        //    return payModel;
        //}



        /// <summary>
        /// 申请支付退款
        /// </summary>
        /// <param name="orderNumber"></param>
        /// <returns></returns>
        [HttpPost]
        [AllowAnonymous]
        public ApiResult Refunds()
        {
            JObject jobj = JObject.Parse(RequestParm.Msg.ToString());
            int contractId = 0; //jobj.GetInt("contractId");
            string outTradeNo = jobj.GetStringValue("OutTradeNo");
            decimal RefundsPrice = jobj.GetDecimal("RefundsPrice");
            if (!string.IsNullOrWhiteSpace(outTradeNo))
            {
                contractId = Convert.ToInt32(outTradeNo[17..]);
            }
            var orderModle = educationContractModule.GetEducationContractModule(contractId);
            var orderRecordModel = educationContractModule.GetOrderRecordList(new RB_Finance_OrderRecord { ContractId = contractId, OutTradeNo = outTradeNo, Type = 2 }).FirstOrDefault();
            if (orderRecordModel == null)
            {
                return ApiResult.Failed("未查询到当前商户订单号对应的支付记录");
            }
            if (RefundsPrice > orderRecordModel.TotalPrice)
            {
                return ApiResult.Failed("退款金额不能大于收款金额");
            }
            var helper = new WxPayHelper(WxPayConst.appid, WxPayConst.mchid, WxPayConst.serialNo, WxPayConst.privateKey);
            var refundNumber = $"{DateTime.Now:yyyyMMddHHmmssfff}{contractId}";
            var payModel = helper.Refunds(orderRecordModel.OutTradeNo, refundNumber, orderModle.CourseName + "退款", Convert.ToInt32(RefundsPrice * 100), Convert.ToInt32(orderRecordModel.TotalPrice * 100), Config.sTenpayNotifyRefundUrl).Result;

            return ApiResult.Success(data: new { Result = payModel, MCh_Id = WxPayConst.mchid });
            // return payModel;
        }




        /// <summary>
        /// 退款通知回调接口
        /// </summary>
        /// <returns>退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功，否则会重试。注意：重试过多会导致微信支付端积压过多通知而堵塞，影响其他正常通知。</returns>
        [HttpPost]
        [HttpGet]
        [AllowAnonymous]
        public RefundsCallbackRespModel RefundsCallback(RefundsCallbackModel wxPayNotifyModel)
        {
            //我没有使用官方的那种验证数据安全性的方法，我解密出来数据之后，直接拿着商户退款订单号再去查询一下订单状态，然后再更新到数据库中。我嫌麻烦……
            var resource = wxPayNotifyModel?.resource ?? new RefundsCallbackResourceModel();
            var decryptStr = AesGcmHelper.AesGcmDecrypt(resource.associated_data, resource.nonce, resource.ciphertext, WxPayConst.APIV3Key);
            var payModel = Common.Plugin.JsonHelper.DeserializeObject<RefundsCallbackDecryptModel>(decryptStr); //decryptStr.ToObject<RefundsCallbackDecryptModel>();

            var viewModel = new RefundsCallbackRespModel();
            if (string.IsNullOrEmpty(payModel.out_trade_no))
            {
                viewModel.code = "FAIL";
                viewModel.message = "数据解密失败";
            }
            else
            {
                //  var payModel = await QueryRefunds(decryptModel.out_refund_no);
                if (payModel != null && payModel.refund_status == "SUCCESS")//生成财务单据
                {
                    UpdateRefundsCallbackFinance(payModel);

                    //int contractId = Convert.ToInt32(payModel.out_trade_no[17..]);
                    //var orderModle = educationContractModule.GetEducationContractModule(contractId);
                    //var orderModel = orderModule.GetClassOrderInfo(orderModle.OrderId);
                    //var oldOrderRecordModel = educationContractModule.GetOrderRecordList(new RB_Finance_OrderRecord { ContractId = contractId, OutTradeNo = payModel.out_trade_no }).FirstOrDefault();

                    //var userInfo = employeeModule.GetEmployeeListModule(new Model.ViewModel.User.Employee_ViewModel { Id = orderModel.EnterID, Group_Id = orderModel.Group_Id }).ToList().FirstOrDefault();
                    //var OriginalFee = Math.Round(Convert.ToDecimal((Convert.ToDecimal(Config.SettlementRate) / 100) * (Convert.ToDecimal(payModel.amount.payer_total) / 100)), 2, MidpointRounding.AwayFromZero);//手续费
                    //RB_Online_Trade_Detail model = new RB_Online_Trade_Detail();
                    //RB_Finance_OrderRecord orderRecordModel = new RB_Finance_OrderRecord
                    //{
                    //    ID = 0,
                    //    Type = 2,
                    //    FinanceId = 0,
                    //    OrderId = orderModle.OrderId,
                    //    ContractId = orderModle.Id,
                    //    CreateDate = System.DateTime.Now,
                    //    RB_Group_Id = orderModle.Group_Id,
                    //    RB_School_Id = orderModle.School_Id,
                    //    OutTradeNo = payModel.out_refund_no,
                    //    OrderRecordId = oldOrderRecordModel?.ID ?? 0,
                    //    TransactionId = payModel.refund_id,
                    //    TotalPrice = Convert.ToDecimal(payModel.amount.payer_total) / 100,
                    //    ServiceFee = OriginalFee
                    //};
                    //bool result = educationContractModule.SetEducationContractFinance(model, orderRecordModel);

                    viewModel.code = "SUCCESS";
                    viewModel.message = "";
                }
                //然后进行数据库更新处理……等等其他操作
            }

            return viewModel;
        }


        [AllowAnonymous]
        public void UpdateRefundsCallbackFinance(RefundsCallbackDecryptModel payModel)
        {
            int contractId = Convert.ToInt32(payModel.out_trade_no[17..]);
            bool isAdd = false;

            if (!Cache.User.UserReidsCache.Exists(Cache.CacheKey.WeChatPay_Callback_Key + payModel.out_trade_no))
            {
                LogHelper.WriteInfo(payModel.out_trade_no + "我是回调：" + System.DateTime.Now);
                Cache.User.UserReidsCache.Set(Cache.CacheKey.WeChatPay_Callback_Key + payModel.out_trade_no, payModel.out_trade_no, 1800);
                isAdd = true;
                Cache.User.UserReidsCache.Set(Cache.CacheKey.WeChatPay_Callback_Key + payModel.out_trade_no, payModel.out_trade_no, 1800);
                var oldOrderRecordModel = educationContractModule.GetOrderRecordList(new RB_Finance_OrderRecord { ContractId = contractId, OutTradeNo = payModel.out_trade_no, Type = 2 }).FirstOrDefault();
                if (oldOrderRecordModel == null || oldOrderRecordModel.ID == 0)
                {
                    isAdd = true;
                }
                if (isAdd)
                {
                    var orderModle = educationContractModule.GetEducationContractModule(contractId);
                    var orderModel = orderModule.GetClassOrderInfoModule(orderModle.OrderId);
                    var payOrderRecordModel = educationContractModule.GetOrderRecordList(new RB_Finance_OrderRecord { ContractId = contractId, OutTradeNo = payModel.out_trade_no, Type = 1 }).FirstOrDefault();
                    var userInfo = employeeModule.GetEmployeeListModule(new Model.ViewModel.User.Employee_ViewModel { Id = orderModel.EnterID, Group_Id = orderModel.Group_Id }).ToList().FirstOrDefault();
                    var OriginalFee = Math.Round(Convert.ToDecimal((Convert.ToDecimal(Config.SettlementRate) / 100) * (Convert.ToDecimal(payModel.amount.payer_total) / 100)), 2, MidpointRounding.AwayFromZero);//手续费
                    RB_Online_Trade_Detail model = new RB_Online_Trade_Detail();
                    RB_Finance_OrderRecord orderRecordModel = new RB_Finance_OrderRecord
                    {
                        ID = 0,
                        Type = 2,
                        FinanceId = 0,
                        OrderId = orderModle.OrderId,
                        ContractId = orderModle.Id,
                        CreateDate = System.DateTime.Now,
                        RB_Group_Id = orderModle.Group_Id,
                        RB_School_Id = orderModle.School_Id,
                        OutTradeNo = payModel.out_refund_no,
                        OrderRecordId = payOrderRecordModel?.ID ?? 0,
                        TransactionId = payModel.refund_id,
                        TotalPrice = Convert.ToDecimal(payModel.amount.payer_refund) / 100,
                        ServiceFee = OriginalFee
                    };
                    bool result = educationContractModule.SetEducationContractFinance(model, orderRecordModel);
                    Cache.User.UserReidsCache.Set(Cache.CacheKey.WeChatPay_Callback_Key + payModel.out_trade_no, payModel.out_trade_no, 1);
                }
            }
            else
            {
                LogHelper.WriteInfo("存在Key" + System.DateTime.Now);
            }

        }

        /// <summary>
        /// 查询退款结果接口
        /// </summary>
        /// <param name="refundNumber">商户系统内部的退款单号，商户系统内部唯一</param>
        /// <returns></returns>
        [HttpGet]
        public async Task<QueryRefundsOrderRespModel> QueryRefunds(string refundNumber)
        {
            var helper = new WxPayHelper(WxPayConst.appid, WxPayConst.mchid, WxPayConst.serialNo, WxPayConst.privateKey);
            var payModel = await helper.QueryRefundsOrder(refundNumber);
            return payModel;
        }




    }
}
