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/alipay"
	"github.com/qit-team/snow-core/log/logger"
	"strconv"
)

// AliInitClient 使用提供的支付请求参数初始化支付宝客户端
func AliInitClient(aliConfig AliPay) (*alipay.Client, error) {
	envConfig := config.GetConf()
	// 初始化支付宝客户端
	// appid:应用ID
	// privateKey:应用私钥,支持PKCS1和PKCS8
	// isProd:是否是正式环境,沙箱环境请选择新版沙箱应用。
	aliClient, aliClientErr := alipay.NewClient(aliConfig.AppId, aliConfig.PrivateKey, envConfig.PayService.IsProd)
	// 自定义配置http请求接收返回结果body大小,默认 10MB
	aliClient.SetBodySize(10) // 没有特殊需求,可忽略此配置

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

	// 设置支付宝请求 公共参数
	//    注意:具体设置哪些参数,根据不同的方法而不同,此处列举出所有设置参数
	aliClient.SetLocation(alipay.LocationShanghai). // 设置时区,不设置或出错均为默认服务器时间
							SetCharset(alipay.UTF8). // 设置字符编码,不设置默认 utf-8
							SetSignType(alipay.RSA2) // 设置签名类型,不设置默认 RSA2

	// 自动同步验签(只支持证书模式)
	// 传入 alipayPublicCert.crt 内容
	aliClient.AutoVerifySign([]byte(aliConfig.AlipayPublicCert))

	// 证书内容
	aliClientErr = aliClient.SetCertSnByContent([]byte(aliConfig.AppPublicCert), []byte(aliConfig.AlipayRootCert), []byte(aliConfig.AlipayPublicCert))
	if aliClient == nil {
		return nil, errors.New("client not initialized")
	}
	if aliClientErr != nil {
		return nil, aliClientErr
	}
	return aliClient, nil
}

// ALiH5PayInfo 支付宝手机网站支付
func ALiH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, error) {
	// 初始化支付宝客户端
	aliClient, err := AliInitClient(payOrderRequest.Ali)
	if err != nil {
		fmt.Println("Failed to get client:", err)
		return "", err
	}
	// 初始化 BodyMap
	amount := float64(payOrderRequest.Amount) / 100.0

	envConfig := config.GetConf()
	bm := make(gopay.BodyMap)
	bm.Set("out_trade_no", payOrderRequest.OrderId).
		Set("total_amount", amount).
		Set("subject", payOrderRequest.Description).
		Set("notify_url", fmt.Sprintf(envConfig.PayService.Host+payCommon.ALI_NOTIFY_URL_TEST+"%d", payOrderRequest.PayChannelId))

	aliRsp, err := aliClient.TradeWapPay(c, bm)
	if err != nil {
		logger.Error(c, "ALiH5PayInfo 发生错误", fmt.Sprintf("错误信息:%s", err.Error()))
		if bizErr, ok := alipay.IsBizError(err); ok {
			return "", bizErr
		}
		return "", err
	}
	return aliRsp, nil
}

// ALiCallBack	支付宝支付回调
func ALiCallBack(notifyReq gopay.BodyMap, aliConfig AliPay) error {
	ok, err := alipay.VerifySignWithCert([]byte(aliConfig.AlipayPublicCert), notifyReq)
	if !ok || err != nil {
		return err
	}

	// 拼装数据触发下游回调,触发下游回调
	orderId, _ := strconv.Atoi(notifyReq.Get("out_trade_no"))
	payerTotal, _ := strconv.Atoi(notifyReq.Get("buyer_pay_amount"))
	//	订单状态
	tradeStatus := notifyReq.Get("trade_status")
	errCode := 0
	msg := ""
	switch tradeStatus {
	case "TRADE_CLOSED":
		errCode = errorcode.ParamError
		msg = "未付款交易超时关闭,或支付完成后全额退款。"
	case "TRADE_SUCCESS":
		errCode = errorcode.Success
		msg = "交易支付成功。"
	case "TRADE_FINISHED":
		errCode = errorcode.Success
		msg = "交易结束,不可退款。"
	}
	if errCode == 0 {
		//	等待买家付款,不走后续回调
		return errors.New("订单状态异常,无法进行后续回调")
	}

	res := thirdpay_notify.NewOrderNotifyWithHandle(int64(orderId), errCode, payerTotal, msg)
	merchantCallback, _ := json.Marshal(res)
	//	记录日志
	go func() {
		payCallback, _ := json.Marshal(notifyReq)
		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(), "ALiCallBack 发生错误", fmt.Sprintf("回调时,下游处理订单失败,返回数据为:%s", string(merchantCallback)))
		return errors.New("回调时,下游处理订单失败")
	}

	return nil
}

// ALiOrderQuery 查询支付宝订单
func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOrderQueryInfo, error) {
	// 初始化支付宝客户端
	aliClient, err := AliInitClient(aliConfig)
	if err != nil {
		fmt.Println("Failed to get client:", err)
		return PayOrderQueryInfo{}, err
	}

	// 请求参数
	bm := make(gopay.BodyMap)
	bm.Set("out_trade_no", OrderNo)

	// 查询订单
	aliRsp, err := aliClient.TradeQuery(ctx, bm)
	if err != nil {
		if bizErr, ok := alipay.IsBizError(err); ok {
			return PayOrderQueryInfo{}, bizErr
		}
		return PayOrderQueryInfo{}, err
	}
	// 同步返回验签
	ok, err := alipay.VerifySyncSignWithCert([]byte(aliConfig.AlipayPublicCert), aliRsp.SignData, aliRsp.Sign)
	if err != nil || !ok {
		return PayOrderQueryInfo{}, errors.New(fmt.Sprintf("验签失败,失败原因:%s", err.Error()))
	}

	//	映射交易状态
	tradeState := ""
	tradeStateDesc := ""
	switch aliRsp.Response.TradeStatus {
	case "TRADE_SUCCESS":
		tradeState = "SUCCESS"
		tradeStateDesc = "交易支付成功"
	case "REFUND":
		tradeState = "REFUND"
		tradeStateDesc = "转入退款"
	case "WAIT_BUYER_PAY":
		tradeState = "NOTPAY"
		tradeStateDesc = "交易创建,等待买家付款"
	case "TRADE_CLOSED":
		tradeState = "CLOSED"
		tradeStateDesc = "未付款交易超时关闭,或支付完成后全额退款"
	}

	amountTotal, _ := strconv.ParseFloat(aliRsp.Response.TotalAmount, 64)
	payerTotal, _ := strconv.ParseFloat(aliRsp.Response.BuyerPayAmount, 64)
	//	构建数据
	outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo)
	return PayOrderQueryInfo{
		AppId:          aliConfig.AppId,
		OutTradeNo:     int64(outTradeNo),
		TransactionId:  aliRsp.Response.TradeNo,
		TradeState:     tradeState,
		TradeStateDesc: tradeStateDesc,
		SuccessTime:    aliRsp.Response.SendPayDate,
		AmountTotal:    int64(amountTotal * 100),
		PayerTotal:     int64(payerTotal * 100),
	}, nil
}

// AliRefundOrder	支付宝退款申请
func AliRefundOrder(ctx context.Context, orderRefundRequest OrderRefundRequest) (OrderRefundInfo, error) {
	// 初始化支付宝客户端
	aliClient, err := AliInitClient(orderRefundRequest.Ali)
	if err != nil {
		fmt.Println("Failed to get client:", err)
		return OrderRefundInfo{}, err
	}

	// 请求参数
	refundAmount := float64(orderRefundRequest.RefundAmount) / 100.0
	bm := make(gopay.BodyMap)
	bm.Set("out_trade_no", orderRefundRequest.OrderId).
		Set("refund_amount", refundAmount).
		Set("refund_reason", orderRefundRequest.RefundReason).
		Set("out_request_no", orderRefundRequest.RefundOrderId)

	// 发起退款请求
	aliRsp, err := aliClient.TradeRefund(ctx, bm)
	if err != nil {
		logger.Error(ctx, "AliRefundOrder 发生错误", fmt.Sprintf("申请退款接口失败,错误信息:%s", err.Error()))
		if bizErr, ok := alipay.IsBizError(err); ok {
			return OrderRefundInfo{}, bizErr
		}
		return OrderRefundInfo{}, err
	}

	refundFee, _ := strconv.ParseFloat(aliRsp.Response.RefundFee, 64)
	outTradeNo, _ := strconv.ParseFloat(aliRsp.Response.OutTradeNo, 64)
	return OrderRefundInfo{
		OutTradeNo:        int64(outTradeNo),
		TransactionId:     aliRsp.Response.TradeNo,
		RefundFee:         int64(refundFee * 100),
		RefundOrderId:     orderRefundRequest.RefundOrderId,
		RefundStatus:      payCommon.PAY_REFUND_STATU_SUCCESS,
		RefundSuccessTime: aliRsp.Response.GmtRefundPay,
	}, nil
}

// AliRefundOrderQuery 支付宝订单退款查询
func AliRefundOrderQuery(ctx context.Context, orderRefundQueryRequest OrderRefundQueryRequest) (OrderRefundInfo, error) {
	// 初始化支付宝客户端
	aliClient, err := AliInitClient(orderRefundQueryRequest.Ali)
	if err != nil {
		fmt.Println("Failed to get client:", err)
		return OrderRefundInfo{}, err
	}

	// 请求参数
	bm := make(gopay.BodyMap)
	bm.Set("out_trade_no", orderRefundQueryRequest.OrderId).
		Set("out_request_no", orderRefundQueryRequest.RefundOrderId).
		Set("query_options", []string{"gmt_refund_pay"})

	// 发起退款查询请求
	aliRsp, err := aliClient.TradeFastPayRefundQuery(ctx, bm)
	if err != nil {
		if bizErr, ok := alipay.IsBizError(err); ok {
			return OrderRefundInfo{}, bizErr
		}
		return OrderRefundInfo{}, err
	}
	refundFee, _ := strconv.ParseFloat(aliRsp.Response.RefundAmount, 64)
	outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo)
	refundOrderId, _ := strconv.Atoi(aliRsp.Response.OutRequestNo)
	return OrderRefundInfo{
		OutTradeNo:        int64(outTradeNo),
		TransactionId:     aliRsp.Response.TradeNo,
		RefundFee:         int64(refundFee * 100),
		RefundOrderId:     int64(refundOrderId),
		RefundStatus:      payCommon.PAY_REFUND_STATU_SUCCESS,
		RefundSuccessTime: aliRsp.Response.GmtRefundPay,
	}, nil
}

// AliCloseOrder	支付宝订单关闭
func AliCloseOrder(ctx context.Context, orderCloseRequest OrderCloseRequest) (OrderCloseInfo, error) {
	// 初始化支付宝客户端
	aliClient, err := AliInitClient(orderCloseRequest.Ali)
	if err != nil {
		fmt.Println("Failed to get client:", err)
		return OrderCloseInfo{}, err
	}
	// 请求参数
	bm := make(gopay.BodyMap)
	bm.Set("out_trade_no", orderCloseRequest.OrderId)

	// 关闭订单
	aliRsp, err := aliClient.TradeClose(ctx, bm)
	if err != nil {
		logger.Error(ctx, "AliCloseOrder 发生错误", fmt.Sprintf("申请退款接口失败,错误信息:%s", err.Error()))
		if bizErr, ok := alipay.IsBizError(err); ok {
			return OrderCloseInfo{}, bizErr
		}
		return OrderCloseInfo{}, err
	}

	outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo)
	return OrderCloseInfo{
		OutTradeNo: int64(outTradeNo),
	}, nil
}