package paymentService

import (
	"PaymentCenter/app/third/paymentService/payCommon"
	"context"
	"errors"
	"fmt"
	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/wechat/v3"
	"github.com/qit-team/snow-core/log/logger"
	"strconv"
	"sync"
	"time"
)

var (
	wxClient  *wechat.ClientV3
	clientErr error
	once      sync.Once
)

// InitClient 使用提供的支付请求参数初始化微信客户端
func InitClient(wxConfig WxPay) {
	once.Do(func() {
		// 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()

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

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

// GetClient 获取已经初始化的微信客户端
func GetClient() (*wechat.ClientV3, error) {
	if wxClient == nil {
		return nil, errors.New("client not initialized")
	}
	if clientErr != nil {
		return nil, clientErr
	}
	return wxClient, nil
}

// WxH5PayInfo 微信H5支付
func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, error) {
	// 初始化微信客户端
	InitClient(payOrderRequest.Wx)

	// 获取微信客户端
	wxClient, err := GetClient()
	if err != nil {
		return "", err
	}
	expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339)
	// 初始化 BodyMap
	bm := make(gopay.BodyMap)
	bm.Set("appid", payOrderRequest.Wx.AppId).
		Set("mchid", payOrderRequest.Wx.MchId).
		Set("description", payOrderRequest.Description).
		Set("out_trade_no", payOrderRequest.OrderId).
		Set("time_expire", expire).
		//Set("notify_url", fmt.Sprintf(payCommon.WX_NOTIFY_URL_TEST+"%d", payOrderRequest.PayChannelId)).
		Set("notify_url", fmt.Sprintf(payCommon.WX_NOTIFY_URL_PROD+"%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 {
	// 初始化微信客户端
	InitClient(wxConfig)

	// 获取微信客户端
	wxClient, err := GetClient()
	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
	}
	//	todo 返回触发下游回调的格式

	return nil
}

// WxOrderQuery 查询微信订单
func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrderQueryInfo, error) {
	// 初始化微信客户端
	InitClient(wxConfig)

	// 获取微信客户端
	wxClient, err := GetClient()
	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) {
	// 初始化微信客户端
	InitClient(orderRefundRequest.Wx)

	// 获取微信客户端
	wxClient, err := GetClient()
	if err != nil {
		return OrderRefundInfo{}, err
	}
	// 初始化 BodyMap
	bm := make(gopay.BodyMap)
	bm.Set("out_trade_no", orderRefundRequest.OrderId).
		Set("sign_type", "MD5").
		// 必填 退款订单号(程序员定义的)
		Set("out_refund_no", orderRefundRequest.RefundOrderId).
		// 选填 退款描述
		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) {
	// 初始化微信客户端
	InitClient(orderRefundQueryRequest.Wx)

	// 获取微信客户端
	wxClient, err := GetClient()
	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) {
	// 初始化微信客户端
	InitClient(orderCloseRequest.Wx)

	// 获取微信客户端
	wxClient, err := GetClient()
	if err != nil {
		return OrderCloseInfo{}, err
	}
	wxRsp, err := wxClient.V3TransactionCloseOrder(ctx, "FY160932049419637602")
	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
}