package paymentService

import (
	"PaymentCenter/app/constants/common"
	"PaymentCenter/app/constants/errorcode"
	"PaymentCenter/app/services/thirdpay/thirdpay_notify"
	"PaymentCenter/app/third/paymentService/payCommon"
	"PaymentCenter/config"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/wechat/v3"
	"github.com/qit-team/snow-core/log/logger"
	"strconv"
	"time"
)

// InitClient 使用提供的支付请求参数初始化微信客户端
func InitClient(wxConfig WxPay) (*wechat.ClientV3, error) {
	// NewClientV3 初始化微信客户端 v3
	// mchid:商户ID 或者服务商模式的 sp_mchid
	// serialNo:商户证书的证书序列号
	// apiV3Key:apiV3Key,商户平台获取
	// privateKey:私钥 apiclient_key.pem 读取后的内容
	wxClient, clientErr := wechat.NewClientV3(
		wxConfig.MchId,
		wxConfig.SerialNo,
		wxConfig.ApiV3Key,
		wxConfig.PrivateKey,
	)
	// 启用自动同步返回验签,并定时更新微信平台API证书(开启自动验签时,无需单独设置微信平台API证书和序列号)
	clientErr = wxClient.AutoVerifySign()
	if wxClient == nil {
		return nil, errors.New("client not initialized")
	}
	if clientErr != nil {
		return nil, clientErr
	}

	// 自定义配置http请求接收返回结果body大小,默认 10MB
	wxClient.SetBodySize(10) // 没有特殊需求,可忽略此配置

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

}

// WxH5PayInfo 微信H5支付
func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, error) {
	// 初始化微信客户端
	wxClient, err := InitClient(payOrderRequest.Wx)
	if err != nil {
		return "", err
	}

	expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339)
	// 初始化 BodyMap
	envConfig := config.GetConf()
	bm := make(gopay.BodyMap)
	bm.Set("appid", payOrderRequest.Wx.AppId).
		Set("mchid", payOrderRequest.Wx.MchId).
		Set("description", payOrderRequest.Description).
		Set("out_trade_no", strconv.FormatInt(payOrderRequest.OrderId, 10)).
		Set("time_expire", expire).
		Set("notify_url", fmt.Sprintf(envConfig.PayService.Host+payCommon.WX_NOTIFY_URL_TEST+"%d", payOrderRequest.PayChannelId)).
		SetBodyMap("amount", func(bm gopay.BodyMap) {
			bm.Set("total", payOrderRequest.Amount).
				Set("currency", "CNY")
		}).
		SetBodyMap("scene_info", func(bm gopay.BodyMap) {
			bm.Set("payer_client_ip", payOrderRequest.PayerClientIp).
				SetBodyMap("h5_info", func(bm gopay.BodyMap) {
					bm.Set("type", "common")
				})
		})

	wxRsp, err := wxClient.V3TransactionH5(c, bm)
	if err != nil {
		return "", err
	}
	if wxRsp.Code != wechat.Success || wxRsp.Response.H5Url == "" {
		logger.Error(c, "WxH5PayInfo 发生错误", fmt.Sprintf("错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
		return "", errors.New(fmt.Sprintf("发起支付失败,失败状态码:%d, 失败原因:%s", wxRsp.Code, wxRsp.Error))
	}
	return wxRsp.Response.H5Url, nil
}

// WxPayCallBack	微信支付回调
func WxPayCallBack(notifyReq *wechat.V3NotifyReq, wxConfig WxPay) error {
	// 初始化微信客户端
	wxClient, err := InitClient(wxConfig)
	if err != nil {
		return err
	}

	// 获取微信平台证书
	certMap := wxClient.WxPublicKeyMap()
	// 验证异步通知的签名
	err = notifyReq.VerifySignByPKMap(certMap)
	if err != nil {
		return err
	}
	var CallBackInfo struct {
		AppId          string `json:"app_id"`           //	应用ID
		Mchid          string `json:"mchid"`            //	商户号
		OutTradeNo     string `json:"out_trade_no"`     //	商户系统内部订单号
		TransactionId  string `json:"transaction_id"`   //	微信系统订单号
		TradeType      string `json:"trade_type"`       //	JSAPI:公众号支付 NATIVE:扫码支付 App:App支付 MICROPAY:付款码支付 MWEB:H5支付 FACEPAY:刷脸支付
		TradeState     string `json:"trade_state"`      //	SUCCESS:支付成功 REFUND:转入退款 NOTPAY:未支付 CLOSED:已关闭 REVOKED:已撤销(付款码支付) USERPAYING:用户支付中(付款码支付) PAYERROR:支付失败(其他原因,如银行返回失败)
		TradeStateDesc string `json:"trade_state_desc"` //	交易状态描述
		SuccessTime    string `json:"success_time"`     //	支付完成时间
		Amount         struct {
			Total      int64 `json:"total"`
			PayerTotal int64 `json:"payer_total"`
		} `json:"amount"`
	}
	// 通用通知解密(推荐此方法)
	err = notifyReq.DecryptCipherTextToStruct(wxConfig.ApiV3Key, &CallBackInfo)
	if err != nil {
		return err
	}

	errCode := 0
	msg := ""
	//	订单状态
	switch CallBackInfo.TradeState {
	case "SUCCESS":
		errCode = errorcode.Success
		msg = "支付成功"
	case "CLOSED":
		errCode = errorcode.ParamError
		msg = "已关闭"
	case "REVOKED":
		errCode = errorcode.ParamError
		msg = "已撤销(付款码支付)"
	case "PAYERROR":
		errCode = errorcode.ParamError
		msg = "支付失败(其他原因,如银行返回失败)"
	}

	//	触发下游回调的格式
	orderId, _ := strconv.Atoi(CallBackInfo.OutTradeNo)
	res := thirdpay_notify.NewOrderNotifyWithHandle(int64(orderId), errCode, int(CallBackInfo.Amount.PayerTotal), msg)
	merchantCallback, _ := json.Marshal(res)
	//	记录日志
	go func() {
		payCallback, _ := json.Marshal(CallBackInfo)
		payParam := "{}"
		status := common.THIRD_ORDER_LOG_STATUS_SUCCESS
		SaveLog(int64(orderId), common.THIRD_ORDER_TYPE_CALL_BACK, string(payCallback), payParam, "{}", status)
	}()

	if res.ErrCode != errorcode.Success {
		logger.Error(context.Background(), "WxPayCallBack 发生错误", fmt.Sprintf("回调时,下游处理订单失败,返回数据为:%s", string(merchantCallback)))
		return errors.New("回调时,下游处理订单失败")
	}

	return nil
}

// WxOrderQuery 查询微信订单
func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrderQueryInfo, error) {
	// 初始化微信客户端
	wxClient, err := InitClient(wxConfig)
	if err != nil {
		return PayOrderQueryInfo{}, err
	}

	wxRsp, err := wxClient.V3TransactionQueryOrder(ctx, payCommon.ORDER_NO_TYPE_ORDER_NO, orderNo)
	if err != nil {
		return PayOrderQueryInfo{}, err
	}
	if wxRsp.Code != wechat.Success {
		return PayOrderQueryInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
	}

	//	映射交易状态
	tradeState := ""
	switch wxRsp.Response.TradeState {
	case "SUCCESS":
		tradeState = "SUCCESS"
	case "REFUND":
		tradeState = "REFUND"
	case "NOTPAY":
		tradeState = "NOTPAY"
	case "CLOSED":
		tradeState = "CLOSED"
	}
	amountTotal := wxRsp.Response.Amount.Total
	payerTotal := wxRsp.Response.Amount.PayerTotal

	outTradeNo, _ := strconv.Atoi(wxRsp.Response.OutTradeNo)
	return PayOrderQueryInfo{
		AppId:          wxRsp.Response.Appid,
		OutTradeNo:     int64(outTradeNo),
		TransactionId:  wxRsp.Response.TransactionId,
		TradeState:     tradeState,
		TradeStateDesc: wxRsp.Response.TradeStateDesc,
		SuccessTime:    wxRsp.Response.SuccessTime,
		AmountTotal:    int64(amountTotal),
		PayerTotal:     int64(payerTotal),
	}, nil
}

// WxOrderRefund	微信退款申请
func WxOrderRefund(ctx context.Context, orderRefundRequest OrderRefundRequest) (OrderRefundInfo, error) {
	// 初始化微信客户端
	wxClient, err := InitClient(orderRefundRequest.Wx)
	if err != nil {
		return OrderRefundInfo{}, err
	}

	// 初始化 BodyMap
	bm := make(gopay.BodyMap)
	bm.Set("out_trade_no", strconv.FormatInt(orderRefundRequest.OrderId, 10)).
		Set("sign_type", "MD5").
		// 必填 退款订单号(程序员定义的)
		Set("out_refund_no", strconv.FormatInt(orderRefundRequest.RefundOrderId, 10)).
		// 选填 退款描述
		Set("reason", orderRefundRequest.RefundReason).
		SetBodyMap("amount", func(bm gopay.BodyMap) {
			// 退款金额:单位是分
			bm.Set("refund", orderRefundRequest.RefundAmount). //实际退款金额
										Set("total", orderRefundRequest.RefundAmount). // 折扣前总金额(不是实际退款数)
										Set("currency", "CNY")
		})
	//    body:参数Body
	refund, err := wxClient.V3Refund(ctx, bm)
	if err != nil {
		return OrderRefundInfo{}, err
	}

	// 将非正常退款异常记录
	if refund.Code != wechat.Success {
		logger.Error(ctx, "WxOrderRefund 发生错误", fmt.Sprintf("申请退款接口失败,错误状态码:%d, 错误信息:%s", refund.Code, refund.Error))
		// 这里时对非正常退款的一些处理message,我们将code统一使用自定义的,然后把message抛出去
		return OrderRefundInfo{}, errors.New(fmt.Sprintf("申请退款接口失败,错误状态码:%d, 错误信息:%s", refund.Code, refund.Error))
	}

	outTradeNo, _ := strconv.Atoi(refund.Response.OutTradeNo)
	outRefundNo, _ := strconv.Atoi(refund.Response.OutRefundNo)
	refundStatus := payCommon.PAY_REFUND_STATU_COMMON
	switch refund.Response.Status {
	case "SUCCESS":
		refundStatus = payCommon.PAY_REFUND_STATU_SUCCESS
	case "CLOSED":
		refundStatus = payCommon.PAY_REFUND_STATU_FAIL
	case "PROCESSING":
		refundStatus = payCommon.PAY_REFUND_STATU_ING
	case "ABNORMAL":
		refundStatus = payCommon.PAY_REFUND_STATU_FAIL
	}
	return OrderRefundInfo{
		OutTradeNo:        int64(outTradeNo),
		TransactionId:     refund.Response.TransactionId,
		RefundFee:         int64(refund.Response.Amount.PayerRefund),
		RefundOrderId:     int64(outRefundNo),
		RefundStatus:      refundStatus,
		RefundSuccessTime: refund.Response.SuccessTime,
	}, nil
}

// WxOrderRefundQuery 微信订单退款查询
func WxOrderRefundQuery(ctx context.Context, orderRefundQueryRequest OrderRefundQueryRequest) (OrderRefundInfo, error) {
	// 初始化微信客户端
	wxClient, err := InitClient(orderRefundQueryRequest.Wx)
	if err != nil {
		return OrderRefundInfo{}, err
	}

	wxRsp, err := wxClient.V3RefundQuery(ctx, strconv.FormatInt(orderRefundQueryRequest.RefundOrderId, 10), nil)
	if err != nil {
		return OrderRefundInfo{}, err
	}
	if wxRsp.Code != wechat.Success {
		return OrderRefundInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
	}
	outTradeNo, _ := strconv.Atoi(wxRsp.Response.OutTradeNo)
	outRefundNo, _ := strconv.Atoi(wxRsp.Response.OutRefundNo)
	refundStatus := payCommon.PAY_REFUND_STATU_COMMON
	switch wxRsp.Response.Status {
	case "SUCCESS":
		refundStatus = payCommon.PAY_REFUND_STATU_SUCCESS
	case "CLOSED":
		refundStatus = payCommon.PAY_REFUND_STATU_FAIL
	case "PROCESSING":
		refundStatus = payCommon.PAY_REFUND_STATU_ING
	case "ABNORMAL":
		refundStatus = payCommon.PAY_REFUND_STATU_FAIL
	}
	return OrderRefundInfo{
		OutTradeNo:        int64(outTradeNo),
		TransactionId:     wxRsp.Response.TransactionId,
		RefundFee:         int64(wxRsp.Response.Amount.PayerRefund),
		RefundOrderId:     int64(outRefundNo),
		RefundStatus:      refundStatus,
		RefundSuccessTime: wxRsp.Response.SuccessTime,
	}, nil
}

// WxCloseOrder 微信订单关闭
func WxCloseOrder(ctx context.Context, orderCloseRequest OrderCloseRequest) (OrderCloseInfo, error) {
	// 初始化微信客户端
	wxClient, err := InitClient(orderCloseRequest.Wx)
	if err != nil {
		return OrderCloseInfo{}, err
	}

	wxRsp, err := wxClient.V3TransactionCloseOrder(ctx, strconv.FormatInt(orderCloseRequest.OrderId, 10))
	if err != nil {
		return OrderCloseInfo{}, err
	}
	if wxRsp.Code != wechat.Success {
		logger.Error(ctx, "WxCloseOrder 发生错误", fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
		return OrderCloseInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
	}
	return OrderCloseInfo{
		OutTradeNo: orderCloseRequest.OrderId,
	}, nil
}