package shanghu

import (
	"duoduo/apis/common"
	"duoduo/apis/shanghu/models"
	"duoduo/models/shanghu"
	"duoduo/tools"
	"duoduo/tools/app"
	"encoding/xml"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/wechat"
	wechatV3 "github.com/go-pay/gopay/wechat/v3"
	"github.com/shopspring/decimal"
	"net/http"
	"reflect"
	"strconv"
	"time"
)

type wechatCallbackResp struct {
	XMLName    xml.Name `xml:"xml"`
	ReturnCode Cdata    `xml:"return_code"`
	ReturnMsg  Cdata    `xml:"return_msg"`
}

type Cdata struct {
	Value string `xml:",cdata"`
}

var GlobalCashOutMap = make(map[string]int)

var (
	successResp = &wechatCallbackResp{ReturnCode: Cdata{Value: "SUCCESS"}, ReturnMsg: Cdata{Value: "OK"}}
	failResp    = &wechatCallbackResp{ReturnCode: Cdata{Value: "FAIL"}, ReturnMsg: Cdata{Value: "数据处理异常"}}
)

func UnifiedOrder(c *gin.Context) {
	var inData models.UnifiedOrderRequest
	var sqlData shanghu.ClientPayTrans
	var outData models.UnifiedOrderReply

	err := c.ShouldBindJSON(&inData)
	if err != nil {
		app.Error(c, 400, err, err.Error())
		return
	}

	if inData.ClientOpenId == "" {
		app.OK(c, nil, app.Success)
		return
	}
	//校验 防止同一笔记录存在
	sqlData.RequestID = inData.RequestId
	if sqlData.GetRequestNum() > 0 {
		app.Error(c, 400, errors.New("交易已存在"), "交易已存在")
		return
	}

	//校验金额
	//检查微信相关参数
	if !inData.Amount.GreaterThan(decimal.NewFromInt(0)) {
		app.Error(c, 400, errors.New("amount:金额必须大于0"), "amount:金额必须大于0")
		return

	}
	if inData.Amount.Round(2).String() != inData.Amount.String() {
		app.Error(c, 400, errors.New("total_fee:金额最多只能保留两位小数"), "total_fee:金额最多只能保留两位小数")
		return
	}

	//创建支付记录
	sqlData.ClientOpenID = inData.ClientOpenId
	sqlData.RequestID = inData.RequestId
	sqlData.CreatedAt = time.Now()
	sqlData.UpdatedAt = time.Now()
	sqlData.Amount = inData.Amount
	sqlData.OutTradeNo = strconv.FormatInt(inData.MerchantCardId, 10) + strconv.FormatInt(time.Now().UnixNano(), 10)
	sqlData.Status = 1 //未支付
	sqlData.MerchantCardID = inData.MerchantCardId
	sqlData.InvitationCode = inData.InvitationCode
	_, err = sqlData.Create()
	if err != nil {
		app.Error(c, 400, err, "创建支付失败")
		return
	}
	fmt.Println(sqlData.OutTradeNo)
	bm := make(gopay.BodyMap)
	bm.Set("nonce_str", common.GetRandomString(32))
	bm.Set("body", "商户卡")
	bm.Set("out_trade_no", sqlData.OutTradeNo)
	bm.Set("total_fee", inData.Amount.Mul(decimal.NewFromInt(100)).IntPart())
	bm.Set("spbill_create_ip", "127.0.0.1")
	bm.Set("notify_url", "https://tao1024.com/v1/client/pay/callback")
	bm.Set("device_info", "WEB")
	bm.Set("trade_type", "JSAPI")
	bm.Set("sign_type", wechat.SignType_MD5)
	bm.Set("openid", inData.ClientOpenId)

	client := NewWechatService()
	//请求支付下单,成功后得到结果
	wxResp, err := client.UnifiedOrder(c, bm)
	if err != nil {
		app.Error(c, 400, err, "下单失败")
		return
	}

	if wxResp.ReturnCode != "SUCCESS" {
		app.Error(c, 400, errors.New(wxResp.ReturnMsg), "下单失败")
		return
	}
	if wxResp.ResultCode != "SUCCESS" {
		app.Error(c, 400, errors.New(wxResp.ErrCode+"--"+wxResp.ErrCodeDes), "下单失败")
		return
	}

	timestamp := strconv.FormatInt(time.Now().Unix(), 10)
	pac := "prepay_id=" + wxResp.PrepayId
	paySign := wechat.GetMiniPaySign("wx8595c589dd736486", wxResp.NonceStr, pac, wechat.SignType_MD5, timestamp, "33c424fAa69942086f82A003e283E9C8")

	outData.Timestamp = timestamp
	outData.NonceStr = wxResp.NonceStr
	outData.Package = pac
	outData.PaySign = paySign
	outData.SignType = wechat.SignType_MD5

	//merchant, count, err := sqlData.GetOpenIdList(pageSize, pageIndex)
	//if err != nil {
	//	app.Error(c, 500, err, err.Error())
	//	return
	//}
	app.OK(c, outData, app.Success)

}

func PayCashOut(c *gin.Context) {
	var inData models.PayCashOutRequest
	var merchantAccountSql shanghu.MerchantAccount
	var clientAccountSql shanghu.MerchantClientAccount
	var cashOut shanghu.CashOut
	var trans []models.TransferDetailList
	var transDetail models.TransferDetailList

	err := c.ShouldBindJSON(&inData)
	if err != nil {
		app.Error(c, 400, err, err.Error())
		return
	}

	_, ok := GlobalCashOutMap[inData.OpenId]
	if ok {
		app.OK(c, nil, "正在提现中...")
		return
	}

	GlobalCashOutMap[inData.OpenId] = 1
	defer func() {
		delete(GlobalCashOutMap, inData.OpenId)
	}()
	if inData.Appid == "" { //appid 不能为空
		app.Error(c, 400, errors.New("Appid不能为空"), "Appid不能为空")
		return
	}

	if inData.Amount.Cmp(decimal.NewFromInt(500)) > 0 {
		app.Error(c, 400, errors.New("单笔金额不能大于500"), "单笔金额不能大于500")
		return
	}

	if inData.Amount.Cmp(decimal.NewFromInt(1)) < 0 {
		app.Error(c, 400, errors.New("单笔金额不能小于1"), "单笔金额不能大于1")
		return
	}

	//
	cashOut.OpenID = inData.OpenId
	cashOut.AppID = inData.Appid
	cashOut.Status = 1    //提现中
	status := []int{1, 3} //提现中、金额待扣减

	cashNum := cashOut.GetCashOutByStatusNum(status)
	if cashNum > 0 {
		app.Error(c, 400, errors.New("有一笔交易正在提现中"), "有一笔交易正在提现中")
		return
	}

	//校验金额是否够
	if inData.AccountType == "client" {
		clientAccountSql.ClientOpenID = inData.OpenId
		clientAccountInfo, err := clientAccountSql.GetClientAccount()
		if err != nil {
			app.Error(c, 400, err, err.Error())
			return
		}
		if inData.Amount.Cmp(clientAccountInfo.Amount) > 0 {
			app.Error(c, 400, errors.New("账号余额不够"), "账号余额不够")
			return
		}
		var client shanghu.MerchantClientUser
		client.Code = "7jb6"
		clientInfo, _ := client.GetUserInfoByCode()
		if clientInfo.ClientOpenID == inData.OpenId {
			cashOut.Fee = decimal.NewFromInt(0)
		} else {
			cashOut.Fee = inData.Amount.Mul(decimal.NewFromFloat32(0.05))
		}

	} else if inData.AccountType == "merchant" {
		merchantAccountSql.MerchantOpenID = inData.OpenId
		merchantAccountInfo, err := merchantAccountSql.GetMerchantAccount()
		if err != nil {
			app.Error(c, 400, err, err.Error())
			return
		}
		if inData.Amount.Cmp(merchantAccountInfo.Amount) > 0 {
			app.Error(c, 400, errors.New("账号余额不够"), "账号余额不够")
			return
		}
		cashOut.Fee = decimal.NewFromInt(0)
	} else {
		app.Error(c, 400, errors.New("账户类型错误"), "账户类型错误")
		return
	}

	clientV3, err := NewWechatServiceV3(inData.Appid)
	if err != nil {
		app.Error(c, 400, err, err.Error())
		return
	}

	userName, err := clientV3.V3EncryptText(inData.UserName)
	if err != nil {
		app.Error(c, 400, err, err.Error())
		return
	}

	transDetail.OutDetailNo = common.GetRandomString(32)
	transDetail.TransferAmount = inData.Amount.Sub(cashOut.Fee).Mul(decimal.NewFromInt(100)).IntPart()
	transDetail.UserName = userName
	transDetail.Openid = inData.OpenId
	transDetail.TransferRemark = "提现"

	trans = append(trans, transDetail)

	partnerTradeNo := common.GetRandomString(32)
	var cashOutCreate shanghu.CashOut
	//创建提现记录
	cashOutCreate.AppID = inData.Appid
	cashOutCreate.Status = 1 //提现中
	cashOutCreate.OpenID = inData.OpenId
	cashOutCreate.Amount = inData.Amount
	cashOutCreate.CreatedAt = time.Now()
	cashOutCreate.UpdatedAt = time.Now()
	cashOutCreate.PartnerTradeNo = partnerTradeNo
	cashOutCreate.Fee = cashOut.Fee
	cashOutInfo, err := cashOutCreate.Create()
	if err != nil {
		app.Error(c, 400, err, err.Error())
		return
	}

	var bMap []gopay.BodyMap
	bm := make(gopay.BodyMap)

	bm.Set("appid", inData.Appid)
	bm.Set("out_batch_no", partnerTradeNo)
	bm.Set("batch_name", "提现")
	bm.Set("batch_remark", "提现")
	bm.Set("total_amount", inData.Amount.Sub(cashOut.Fee).Mul(decimal.NewFromInt(100)).IntPart())
	bm.Set("total_num", 1)
	bMap = append(bMap, structToMap(&transDetail))

	bm.Set("transfer_detail_list", bMap)
	bm.Set("transfer_scene_id", "1001")

	fmt.Println(bm.JsonBody())

	reply, err := clientV3.V3Transfer(c, bm)
	if err != nil {
		cashOut.ID = cashOutInfo.ID
		cashOut.FailRes = err.Error()
		cashOut.Status = 2 //提现失败
		cashOut.UpdateMerchantStatus()
		app.Error(c, 500, err, err.Error())
		return
	}

	if reply.Code != 0 {
		cashOut.ID = cashOutInfo.ID
		cashOut.FailRes = reply.Error
		cashOut.Status = 2 //提现失败
		cashOut.UpdateMerchantStatus()
		app.Error(c, 500, errors.New(reply.Error), reply.Error)
		return
	}

	cashOut.WxPartnerTradeNo = reply.Response.BatchId
	cashOut.PartnerTradeNo = reply.Response.OutBatchNo
	cashOut.UpdateCashOutWxBachNo()

	app.OK(c, nil, app.Success)

}

func PayCallBack(c *gin.Context) {
	var payLog shanghu.PayCallbackLog
	var payTrans shanghu.ClientPayTrans
	wxNotify, err := wechat.ParseNotifyToBodyMap(c.Request)
	if err != nil {
		c.XML(http.StatusOK, failResp)
		return
	}

	//通知回调log
	payLog.CallBackLog = wxNotify.JsonBody()
	payLog.ThirdTradeNo = wxNotify.Get("transaction_id")
	payLog.OutTradeNo = wxNotify.Get("out_trade_no")
	payLog.CreatedAt = time.Now()
	payLog.UpdatedAt = time.Now()

	_, err = payLog.Create()
	if err != nil {
		c.XML(http.StatusOK, failResp)
		return
	}

	if wxNotify.Get("return_code") != "SUCCESS" || wxNotify.Get("result_code") != "SUCCESS" {
		payLog.ErrLog = "微信返回错误:" + wxNotify.Get("return_code") + "--" + wxNotify.Get("result_code")
		payLog.UpdateMerchant()
		c.XML(http.StatusOK, failResp)
		return
	}

	//校验金额
	payTrans.OutTradeNo = wxNotify.Get("out_trade_no")
	payTransInfo, err := payTrans.GetPayTransByTradeNo()
	if err != nil {
		payLog.ErrLog = "查询交易信息错误:" + " err=" + err.Error()
		payLog.UpdateMerchant()
		c.XML(http.StatusOK, failResp)
		return
	}
	// 判断金额与支付流水是否一致
	totalFee, err := decimal.NewFromString(wxNotify.Get("total_fee"))
	if err != nil {
		payLog.ErrLog = "解析总金额报错:" + "err=" + err.Error()
		payLog.UpdateMerchant()
		c.XML(http.StatusOK, failResp)
		return
	}
	if !totalFee.Equal(payTransInfo.Amount.Mul(decimal.NewFromInt(100))) {
		payLog.ErrLog = "验证金额报错:total_fee=" + wxNotify.Get("total_fee") + " amount=" + payTransInfo.Amount.String()
		payLog.UpdateMerchant()
		c.XML(http.StatusOK, failResp)
		return
	}
	// 解析支付时间
	timeEnd, err := time.ParseInLocation("20060102150405", wxNotify.Get("time_end"), tools.TimeLocation)
	if err != nil {
		payLog.ErrLog = "付款时间解析出错:err=" + err.Error()
		payLog.UpdateMerchant()
		c.XML(http.StatusOK, failResp)
		return
	}

	payTrans.ThirdTradeNo = wxNotify.Get("transaction_id")
	payTrans.Status = 2 //支付成功
	payTrans.PayTime = timeEnd
	payTrans.AccountStatus = 1 //未分账

	err = payTrans.UpdatePayTransByTradeNo()
	if err != nil {
		payLog.ErrLog = "更新支付状态失败:err=" + err.Error()
		payLog.UpdateMerchant()
		c.XML(http.StatusOK, failResp)
		return
	}

	c.XML(http.StatusOK, successResp)

}

func NewWechatService() *wechat.Client {

	client := wechat.NewClient("wx8595c589dd736486", "1670841410", "33c424fAa69942086f82A003e283E9C8", true)
	//设置国家
	client.SetCountry(wechat.China)

	return client
}

func NewWechatServiceTwo(appid, mchId, apiKey string) *wechat.Client {

	client := wechat.NewClient(appid, mchId, apiKey, true)
	//设置国家
	client.SetCountry(wechat.China)

	return client
}

func NewWechatServiceAppid(appid string) *wechat.Client {

	client := wechat.NewClient(appid, "1501641641", "1RKRJBVH4vaRrF0XPW9GX2M3ZSImukIz", true)
	//设置国家
	client.SetCountry(wechat.China)

	return client
}

func NewWechatServiceV3(appid string) (*wechatV3.ClientV3, error) {

	key := `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7Q/aQNEHIZC6p
BmUPx9dgt56vC9l1aDT0MG/PovJ2NkihIxccsEkKvxtUxBGQ5ysNRXWG22qgJE38
4NYb7nmxsAl9XNWxix5HsjAnWtdCiMp4yRxM3xeDQp0sp25zK5OU+GXPhJ7shBoB
dlJd9BtIMK5URKVRU7JyUhn2+/FFPEVQ9pbL+cnpDesC5L+uQVqKyYK9hW0x7fy/
RKwi8T8d37CdfIybywqlUFUzXP3BUYuR1L0dVikD6is3Aq8/+DNs68f5wLOvjdow
kBTuegtDd7Km2y3Tqnf3gwHxAYW1KoSuKijV8SWRuQnfTf84nWfy9lryUMn+dZHt
2SX8LyQRAgMBAAECggEAQViM8HMbxWaYFalCmMgecwSAHgsffeW4HMHOMoFk5DU7
EOeiyAMH6fUX/3NPweW40y+6vC1SvsEMacK3VBXaZ1PLa/B6LTMjhNc8EG+VkAUV
yiI7euOaW9Zh7FQcqZm6LRCkzk4z+sp6HKqCQYDOCFnca1Fs8r0nFtdpchMmdQbi
cl6uVj41hCVV0xWdPMb5LXTnhAuckVDW8M2oHavjdbKzL9H//WEjYqDZ76eLwvg3
/89OamrXjUyd+lquSYuuG+3/HzI3a2QXeKwBysskmAwzEZrAQe9O3V2qDks7jFYP
RcqGnwpZSRkEpzUiEDrWVamVHjWwOQy6J7h8jEVpxQKBgQDy980Tyhuhnp6SIxmV
FnAnQHm06abq+gLQvEYwTcE/oYHVbd2cOrn/tQ5QN60JSGGaHvio1Fh1x4oM/iZ3
Pw0nGZTBpwiPDnk0rJSliqOAYejua/nITpaXE5v/yu7SeBrvaP3pRJKtm2sROs79
GvFiA4F0o7utqSG45yf32hLzWwKBgQDFT09RXKWHVjDFnv8o6NahSnH1Mroy9AKW
WW3PlYPOtMW6eIPt9Pw2hSYyEVTc+FPRbtZploTki3Aq3FU1qVXMwhJb464pg6gW
OKmLXdPXIkBQ+g6REZpseWrTlNM99jebURXx4o+S6am14LwGV98m5Yz7FfEn2uWu
9A7LUyb+AwKBgQC42CNSEenRnH5hCYkV7nsmsZumMjhF/iyUJxhy+USzJylCeZIm
oFsPGeyoxKLFNCbk/PkKYHpoKIVzewtCn1pfS8vrRCtzwnwdwcpY4s+fBV4TvVvL
s1ZH26hMA1SMFMnRKBw8EmpQ37Ol6Qq+bngDrE0ZstM+vDSmml3C50qRjwKBgFAV
aLQ1Tlon+ZO8fQQ1vSep96b19+1GbOZpVdCzdtQnOzn8QFAM53GZiW846aDmid6v
hgdFOJsqnVRIKa2mFVUOUDVLrBzdexPJ28bdRmZDWKeFVvQ6mNr+TQWjmjnD/b3k
o2uR8YRHosJXfPl2IPTApwAiX8c1aZQhKwALt//RAoGBAIYu48CyWdlwpFdvqBJc
KF8wna5Xm20ETxNl5hN9ffWRfIB0DWUnW+HTr3fD0KOxUuA8lIjpi9QwxaQVva6C
nBV1PRxCDZ6Wk7G9XvT7rEr4cOFWsR8TEkI9IY4qbK7QXZworCrM9EhKdrt36n+q
KRm8Wc217XJMOFZj7zErPmNF
-----END PRIVATE KEY-----`
	client, err := wechatV3.NewClientV3("1670841410", "38B1AD453A0DD1025DE4DAC6C4494CD1A134EE15", "33c424fAa69942086f82A003e283E9C8", key)
	if err != nil {
		return nil, err
	}

	// 启用自动同步返回验签,并定时更新微信平台API证书(开启自动验签时,无需单独设置微信平台API证书和序列号)
	err = client.AutoVerifySign()
	if err != nil {
		return nil, err
	}

	// 打开Debug开关,输出日志,默认是关闭的
	//client.DebugSwitch = gopay.DebugOn

	return client, nil
}
func structToMap(obj interface{}) gopay.BodyMap {
	result := make(gopay.BodyMap)
	value := reflect.ValueOf(obj).Elem() // 获取指针的值
	typ := value.Type()                  // 获取类型信息

	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)              // 获取字段信息
		tag := field.Tag.Get("json")       // 获取标签(如果有)
		if tag != "" && !field.Anonymous { // 只处理非匿名字段且有标签的情况
			key := field.Name // 默认使用字段名作为Key
			if tag != "-" {   // 若标签不等于-则使用标签作为Key
				key = tag
			}
			result[key] = value.Field(i).Interface() // 存入Map
		}
	}

	return result
}