package payment

import (
	"bytes"
	"context"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	bizpayment "kra/internal/biz/payment"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/go-pay/gopay"
	gopayWechatV3 "github.com/go-pay/gopay/wechat/v3"
)

type wechatV3Adapter struct{}

func (a *wechatV3Adapter) Create(ctx context.Context, req *bizpayment.PaymentRequest, c map[string]any) (*bizpayment.PaymentResult, error) {
	if req == nil {
		return nil, errors.New("微信支付 v3 下单请求为空")
	}
	client, err := newWechatV3Client(c, "create_url")
	if err != nil {
		return nil, err
	}
	bm := gopay.BodyMap{
		"appid":        firstAny(c, "app_id", "appid"),
		"mchid":        firstAny(c, "merchant_id", "mch_id", "mchid"),
		"description":  req.Subject,
		"out_trade_no": req.TradeNo,
		"notify_url":   req.NotifyURL,
	}
	bm.SetBodyMap("amount", func(amount gopay.BodyMap) {
		amount.Set("total", req.Amount).Set("currency", strings.ToUpper(req.Currency))
	})
	mergeGoPayExtras(bm, req.Extra,
		"appid", "mchid", "description", "out_trade_no", "notify_url", "amount", "payer",
		"trade_type", "pay_type", "method", "openid", "open_id", "auth_code", "authcode", "barcode",
	)

	tradeType := normalizeWechatV3TradeType(firstAny(req.Extra, "trade_type", "pay_type", "method"))
	if tradeType == "" {
		tradeType = normalizeWechatV3TradeType(firstAny(c, "trade_type", "pay_type", "method"))
	}
	switch tradeType {
	case "", "jsapi", "mini", "miniprogram", "mini_program", "applet":
		if openID := firstAny(req.Extra, "openid", "open_id"); openID != "" {
			bm.SetBodyMap("payer", func(payer gopay.BodyMap) { payer.Set("openid", openID) })
		}
		rsp, callErr := client.V3TransactionJsapi(ctx, bm)
		if callErr != nil {
			return nil, callErr
		}
		if rsp == nil {
			return nil, errors.New("微信支付 v3 下单响应为空")
		}
		return wechatV3PrepayCreateResult(client, firstAny(c, "app_id", "appid"), tradeType, req.TradeNo, rsp)
	case "app":
		rsp, callErr := client.V3TransactionApp(ctx, bm)
		if callErr != nil {
			return nil, callErr
		}
		if rsp == nil {
			return nil, errors.New("微信支付 v3 下单响应为空")
		}
		return wechatV3PrepayCreateResult(client, firstAny(c, "app_id", "appid"), "app", req.TradeNo, rsp)
	case "native", "qr", "qrcode":
		rsp, callErr := client.V3TransactionNative(ctx, bm)
		if callErr != nil {
			return nil, callErr
		}
		if rsp == nil {
			return nil, errors.New("微信支付 v3 下单响应为空")
		}
		return wechatV3CreateResult(req.TradeNo, rsp.Code, rsp.SignInfo, rsp.Response, rsp.ErrResponse, rsp.Error)
	case "h5", "mweb":
		rsp, callErr := client.V3TransactionH5(ctx, bm)
		if callErr != nil {
			return nil, callErr
		}
		if rsp == nil {
			return nil, errors.New("微信支付 v3 下单响应为空")
		}
		return wechatV3CreateResult(req.TradeNo, rsp.Code, rsp.SignInfo, rsp.Response, rsp.ErrResponse, rsp.Error)
	case "micropay", "micro_pay", "codepay", "code_pay", "barcode", "barcode_pay", "facepay", "face_pay":
		authCode := strings.TrimSpace(firstAny(req.Extra, "auth_code", "authcode", "barcode"))
		if authCode == "" {
			return nil, errors.New("微信支付 v3 付款码支付缺少 auth_code")
		}
		bm.SetBodyMap("payer", func(payer gopay.BodyMap) { payer.Set("auth_code", authCode) })
		rsp, callErr := client.V3TransactionCodePay(ctx, bm)
		if callErr != nil {
			return nil, callErr
		}
		result, err := wechatV3CodePayResult(req.TradeNo, rsp)
		if err != nil {
			return nil, err
		}
		if result.Amount > 0 && result.Amount != req.Amount {
			return nil, errors.New("微信支付 v3 付款码响应金额不匹配")
		}
		if result.Currency != "" && req.Currency != "" && !strings.EqualFold(result.Currency, req.Currency) {
			return nil, errors.New("微信支付 v3 付款码响应币种不匹配")
		}
		if rsp.Response.Appid != "" && rsp.Response.Appid != firstAny(c, "app_id", "appid") {
			return nil, errors.New("微信支付 v3 付款码响应 appid 不匹配")
		}
		if rsp.Response.Mchid != "" && rsp.Response.Mchid != firstAny(c, "merchant_id", "mch_id", "mchid") {
			return nil, errors.New("微信支付 v3 付款码响应商户号不匹配")
		}
		return result, nil
	default:
		return nil, fmt.Errorf("微信支付 v3 不支持交易类型 %q", tradeType)
	}
}

func normalizeWechatV3TradeType(value string) string {
	return strings.NewReplacer(".", "", "-", "_", " ", "_").Replace(strings.ToLower(strings.TrimSpace(value)))
}

func wechatV3CodePayResult(tradeNo string, rsp *gopayWechatV3.CodePayRsp) (*bizpayment.PaymentResult, error) {
	if rsp == nil {
		return nil, errors.New("微信支付 v3 付款码响应为空")
	}
	if rsp.Code != gopayWechatV3.Success {
		return nil, wechatV3APIError("付款码支付", rsp.Code, rsp.ErrResponse, rsp.Error)
	}
	if rsp.Response == nil {
		return nil, errors.New("微信支付 v3 付款码响应缺少订单")
	}
	order := rsp.Response
	if returnedTradeNo := strings.TrimSpace(order.OutTradeNo); returnedTradeNo == "" {
		return nil, errors.New("微信支付 v3 付款码响应缺少 out_trade_no")
	} else if returnedTradeNo != tradeNo {
		return nil, errors.New("微信支付 v3 付款码响应的商户订单号不匹配")
	}
	result := &bizpayment.PaymentResult{
		Provider:        bizpayment.PaymentWechatV3,
		Status:          normalizePaymentStatus(order.TradeState, "pending"),
		TradeNo:         tradeNo,
		ProviderTradeNo: strings.TrimSpace(order.TransactionId),
		Payload:         wechatV3ResponsePayload(rsp.SignInfo, order),
	}
	if order.Amount != nil {
		result.Amount = int64(order.Amount.Total)
		result.Currency = strings.ToUpper(order.Amount.Currency)
		result.PayerCurrency = strings.ToUpper(order.Amount.PayerCurrency)
		if result.PayerCurrency == "" {
			result.PayerCurrency = result.Currency
		}
		// GoPay's Amount.PayerTotal is an int, so a missing JSON field and an
		// explicit zero are otherwise indistinguishable. Inspect the signed
		// response body before marking the amount breakdown as authoritative.
		payloadObject := jsonObject(wechatV3ResponsePayload(rsp.SignInfo, order))
		if amountObject, ok := valueAtPath(payloadObject, "amount").(map[string]any); ok {
			if payerTotal, payerOK, payerErr := parseJSONInteger(amountObject, "payer_total"); payerErr != nil {
				return nil, fmt.Errorf("解析微信支付 v3 付款码 amount.payer_total: %w", payerErr)
			} else if payerOK && payerTotal >= 0 && payerTotal <= result.Amount {
				result.PayerPaidAmount = payerTotal
				result.CashPaidAmount = payerTotal
				result.DiscountAmount = result.Amount - payerTotal
				result.AmountBreakdownKnown = true
			}
		}
	}
	if result.Status == "success" {
		if result.ProviderTradeNo == "" {
			return nil, errors.New("微信支付 v3 付款码响应缺少 transaction_id")
		}
		if result.Amount <= 0 || result.Currency == "" {
			return nil, errors.New("微信支付 v3 付款码响应缺少有效金额")
		}
	}
	return result, nil
}

func (a *wechatV3Adapter) Query(ctx context.Context, tradeNo string, c map[string]any) (*bizpayment.PaymentResult, error) {
	tradeNo = strings.TrimSpace(tradeNo)
	if tradeNo == "" {
		return nil, errors.New("微信支付 v3 查单缺少 out_trade_no")
	}
	client, err := newWechatV3Client(c, "query_url")
	if err != nil {
		return nil, err
	}
	rsp, err := client.V3TransactionQueryOrder(ctx, gopayWechatV3.OutTradeNo, tradeNo)
	if err != nil {
		return nil, err
	}
	if rsp == nil {
		return nil, errors.New("微信支付 v3 查单响应为空")
	}
	if rsp.Code != gopayWechatV3.Success {
		return nil, wechatV3APIError("查单", rsp.Code, rsp.ErrResponse, rsp.Error)
	}
	if rsp.Response == nil {
		return nil, errors.New("微信支付 v3 查单响应缺少订单")
	}

	order := rsp.Response
	payload := wechatV3ResponsePayload(rsp.SignInfo, order)
	result := &bizpayment.PaymentResult{
		Provider:        bizpayment.PaymentWechatV3,
		Status:          normalizePaymentStatus(order.TradeState, "pending"),
		TradeNo:         order.OutTradeNo,
		ProviderTradeNo: order.TransactionId,
		Payload:         payload,
	}
	if result.TradeNo == "" {
		result.TradeNo = tradeNo
	} else if result.TradeNo != tradeNo {
		return nil, errors.New("微信支付 v3 查询响应的 out_trade_no 不匹配")
	}
	if result.Status != "success" {
		return result, nil
	}
	if result.ProviderTradeNo == "" {
		return nil, errors.New("微信支付 v3 查询响应缺少 transaction_id")
	}
	if order.Amount == nil || order.Amount.Total <= 0 {
		return nil, errors.New("微信支付 v3 查询响应缺少 amount.total")
	}
	result.Amount = int64(order.Amount.Total)
	result.Currency = strings.ToUpper(order.Amount.Currency)
	if result.Currency == "" {
		return nil, errors.New("微信支付 v3 查询响应缺少 amount.currency")
	}
	if err := populateWechatV3Breakdown(result, jsonObject(payload)); err != nil {
		return nil, err
	}
	return result, nil
}

func (a *wechatV3Adapter) Refund(ctx context.Context, req *bizpayment.PaymentRefundRequest, c map[string]any) (*bizpayment.PaymentResult, error) {
	if req == nil {
		return nil, errors.New("微信支付 v3 退款请求为空")
	}
	tradeNo := strings.TrimSpace(req.TradeNo)
	if tradeNo == "" {
		return nil, errors.New("微信支付 v3 退款请求缺少 out_trade_no")
	}
	refundNo := strings.TrimSpace(req.RefundNo)
	if refundNo == "" {
		return nil, errors.New("微信支付 v3 退款请求缺少 out_refund_no")
	}
	if req.Amount <= 0 || req.TotalAmount <= 0 || req.Amount > req.TotalAmount {
		return nil, errors.New("微信支付 v3 退款金额无效")
	}
	client, err := newWechatV3Client(c, "refund_url")
	if err != nil {
		return nil, err
	}
	bm := gopay.BodyMap{"out_trade_no": tradeNo, "out_refund_no": refundNo}
	bm.SetBodyMap("amount", func(values gopay.BodyMap) {
		values.Set("refund", req.Amount).Set("total", req.TotalAmount).Set("currency", strings.ToUpper(req.Currency))
	})
	mergeGoPayConfigExtras(bm, c, "refund_extra", "out_trade_no", "out_refund_no", "amount")
	rsp, err := client.V3Refund(ctx, bm)
	if err != nil {
		return nil, err
	}
	if rsp == nil {
		return nil, errors.New("微信支付 v3 退款响应为空")
	}
	if rsp.Code != gopayWechatV3.Success {
		return nil, wechatV3APIError("退款", rsp.Code, rsp.ErrResponse, rsp.Error)
	}
	if rsp.Response == nil {
		return nil, errors.New("微信支付 v3 退款响应缺少退款单")
	}

	refund := rsp.Response
	refundID, err := validateWechatV3RefundIdentity(refund, tradeNo, refundNo)
	if err != nil {
		return nil, err
	}
	result := &bizpayment.PaymentResult{
		Provider:        bizpayment.PaymentWechatV3,
		Status:          normalizeRefundState(refund.Status),
		TradeNo:         tradeNo,
		ProviderTradeNo: refundID,
		Payload:         wechatV3ResponsePayload(rsp.SignInfo, refund),
	}
	if refund.Amount == nil {
		return nil, errors.New("微信支付 v3 退款响应缺少金额")
	}
	result.Amount = int64(refund.Amount.Refund)
	result.Currency = strings.ToUpper(strings.TrimSpace(refund.Amount.Currency))
	if result.Amount != req.Amount {
		return nil, errors.New("微信支付 v3 退款响应金额不匹配")
	}
	if result.Currency == "" {
		return nil, errors.New("微信支付 v3 退款响应缺少币种")
	}
	if req.Currency != "" && !strings.EqualFold(result.Currency, req.Currency) {
		return nil, errors.New("微信支付 v3 退款响应币种不匹配")
	}
	return result, nil
}

func validateWechatV3RefundIdentity(refund *gopayWechatV3.RefundOrderResponse, tradeNo, refundNo string) (string, error) {
	if refund == nil {
		return "", errors.New("微信支付 v3 退款响应缺少退款单")
	}
	tradeNo = strings.TrimSpace(tradeNo)
	if tradeNo == "" {
		return "", errors.New("微信支付 v3 退款请求缺少 out_trade_no")
	}
	refundNo = strings.TrimSpace(refundNo)
	if refundNo == "" {
		return "", errors.New("微信支付 v3 退款请求缺少 out_refund_no")
	}
	returnedTradeNo := strings.TrimSpace(refund.OutTradeNo)
	if returnedTradeNo == "" {
		return "", errors.New("微信支付 v3 退款响应缺少 out_trade_no")
	}
	if returnedTradeNo != tradeNo {
		return "", errors.New("微信支付 v3 退款响应的 out_trade_no 不匹配")
	}
	returnedRefundNo := strings.TrimSpace(refund.OutRefundNo)
	if returnedRefundNo == "" {
		return "", errors.New("微信支付 v3 退款响应缺少 out_refund_no")
	}
	if returnedRefundNo != refundNo {
		return "", errors.New("微信支付 v3 退款响应的 out_refund_no 不匹配")
	}
	refundID := strings.TrimSpace(refund.RefundId)
	if refundID == "" {
		return "", errors.New("微信支付 v3 退款响应缺少 refund_id")
	}
	return refundID, nil
}

func (a *wechatV3Adapter) Callback(ctx context.Context, callback *bizpayment.PaymentCallback, c map[string]any) (*bizpayment.PaymentResult, error) {
	if callback == nil {
		return nil, errors.New("微信支付 v3 回调为空")
	}
	apiV3Key := firstAny(c, "api_v3_key", "api_v3key", "apiv3_key")
	if apiV3Key == "" {
		return nil, errors.New("微信支付 v3 缺少 api_v3_key")
	}
	client, err := newWechatV3Client(c, "")
	if err != nil {
		return nil, err
	}
	request, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://callback.local", bytes.NewReader(callback.Body))
	if err != nil {
		return nil, err
	}
	for key, value := range callback.Headers {
		request.Header.Set(key, value)
	}
	notify, err := gopayWechatV3.V3ParseNotify(request)
	if err != nil {
		return nil, err
	}
	if notify.SignInfo == nil {
		return nil, errors.New("微信支付 v3 回调缺少签名信息")
	}
	platformKeys := client.WxPublicKeyMap()
	if _, exists := platformKeys[notify.SignInfo.HeaderSerial]; !exists {
		for serial, key := range platformKeys {
			if strings.EqualFold(serial, notify.SignInfo.HeaderSerial) {
				platformKeys[notify.SignInfo.HeaderSerial] = key
				break
			}
		}
	}
	if err = notify.VerifySignByPKMap(platformKeys); err != nil {
		return nil, err
	}
	pay, err := notify.DecryptPayCipherText(apiV3Key)
	if err != nil {
		return nil, err
	}
	if callback.Query == nil {
		callback.Query = map[string]string{}
	}
	callback.Query["event_id"] = notify.Id
	status := "pending"
	if pay.TradeState == gopayWechatV3.TradeStateSuccess {
		status = "success"
	}
	merchantID := firstAny(c, "merchant_id", "mch_id", "mchid")
	if pay.Mchid != "" && pay.Mchid != merchantID {
		return nil, errors.New("微信支付 v3 回调商户号不匹配")
	}
	appID := firstAny(c, "app_id", "appid")
	if pay.Appid != "" && pay.Appid != appID {
		return nil, errors.New("微信支付 v3 回调 appid 不匹配")
	}
	plain, _ := json.Marshal(pay)
	return &bizpayment.PaymentResult{
		Provider:        bizpayment.PaymentWechatV3,
		Status:          status,
		TradeNo:         pay.OutTradeNo,
		ProviderTradeNo: pay.TransactionId,
		Payload:         plain,
	}, nil
}

func newWechatV3Client(c map[string]any, endpointKey string) (*gopayWechatV3.ClientV3, error) {
	merchantID := firstAny(c, "merchant_id", "mch_id", "mchid")
	serialNo := firstAny(c, "serial_no", "merchant_serial_no", "certificate_serial_no")
	apiV3Key := firstAny(c, "api_v3_key", "api_v3key", "apiv3_key")
	privateKey := firstAny(c, "private_key", "merchant_private_key")
	if merchantID == "" || serialNo == "" || apiV3Key == "" || privateKey == "" {
		return nil, errors.New("微信支付 v3 缺少 merchant_id、serial_no、api_v3_key 或 private_key")
	}
	client, err := gopayWechatV3.NewClientV3(merchantID, serialNo, apiV3Key, privateKey)
	if err != nil {
		return nil, err
	}

	platformCert := firstAny(c, "platform_cert", "platform_certificate", "wechatpay_platform_cert")
	if platformCert == "" {
		return nil, errors.New("微信支付 v3 缺少 platform_cert")
	}
	platformSerial := firstAny(c, "platform_serial_no", "platform_cert_serial", "wechatpay_serial_no")
	if platformSerial == "" {
		platformSerial, err = wechatV3PlatformSerial(platformCert)
		if err != nil {
			return nil, err
		}
	}
	if err = client.AutoVerifySignByCert([]byte(platformCert), platformSerial); err != nil {
		return nil, err
	}
	client.SetBodySize(8)
	client.GetHttpClient().SetTimeout(20 * time.Second)

	if endpointKey != "" {
		if endpoint := strings.TrimSpace(text(c, endpointKey)); endpoint != "" {
			parsed, parseErr := url.Parse(endpoint)
			if parseErr != nil || parsed.Scheme == "" || parsed.Host == "" {
				return nil, fmt.Errorf("微信支付 v3 %s 配置无效", endpointKey)
			}
			if parsed.Path != "" && parsed.Path != "/" || parsed.RawQuery != "" || parsed.Fragment != "" {
				return nil, fmt.Errorf("微信支付 v3 %s 只能配置 scheme 和 host", endpointKey)
			}
			httpClient := client.GetHttpClient()
			httpClient.SetTransport(&wechatV3EndpointTransport{base: httpClient.HttpClient.Transport, endpoint: parsed})
			return client, nil
		}
	}
	if baseURL := strings.TrimRight(firstAny(c, "base_url", "api_base_url", "proxy_url"), "/"); baseURL != "" {
		parsed, parseErr := url.Parse(baseURL)
		if parseErr != nil || parsed.Scheme == "" || parsed.Host == "" {
			return nil, errors.New("微信支付 v3 base_url 配置无效")
		}
		client.SetProxyHost(baseURL)
	}
	return client, nil
}

type wechatV3EndpointTransport struct {
	base     http.RoundTripper
	endpoint *url.URL
}

func (t *wechatV3EndpointTransport) RoundTrip(request *http.Request) (*http.Response, error) {
	base := t.base
	if base == nil {
		base = http.DefaultTransport
	}
	clone := request.Clone(request.Context())
	target := *request.URL
	target.Scheme = t.endpoint.Scheme
	target.Host = t.endpoint.Host
	target.User = t.endpoint.User
	clone.URL = &target
	clone.Host = ""
	return base.RoundTrip(clone)
}

func wechatV3PlatformSerial(certificate string) (string, error) {
	block, _ := pem.Decode([]byte(certificate))
	if block == nil || block.Type != "CERTIFICATE" {
		return "", errors.New("微信支付 v3 platform_cert 格式错误")
	}
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return "", fmt.Errorf("解析微信支付 v3 platform_cert: %w", err)
	}
	return strings.ToUpper(cert.SerialNumber.Text(16)), nil
}

func wechatV3CreateResult(tradeNo string, code int, signInfo *gopayWechatV3.SignInfo, response any, errResponse gopayWechatV3.ErrResponse, rawError string) (*bizpayment.PaymentResult, error) {
	if code != gopayWechatV3.Success {
		return nil, wechatV3APIError("下单", code, errResponse, rawError)
	}
	if response == nil {
		return nil, errors.New("微信支付 v3 下单响应缺少预支付信息")
	}
	return &bizpayment.PaymentResult{
		Provider: bizpayment.PaymentWechatV3,
		Status:   "created",
		TradeNo:  tradeNo,
		Payload:  wechatV3ResponsePayload(signInfo, response),
	}, nil
}

func wechatV3PrepayCreateResult(client *gopayWechatV3.ClientV3, appID, tradeType, tradeNo string, rsp *gopayWechatV3.PrepayRsp) (*bizpayment.PaymentResult, error) {
	if rsp == nil {
		return nil, errors.New("微信支付 v3 下单响应为空")
	}
	result, err := wechatV3CreateResult(tradeNo, rsp.Code, rsp.SignInfo, rsp.Response, rsp.ErrResponse, rsp.Error)
	if err != nil {
		return nil, err
	}
	if rsp.Response == nil || strings.TrimSpace(rsp.Response.PrepayId) == "" {
		return nil, errors.New("微信支付 v3 下单响应缺少 prepay_id")
	}

	var payParams any
	switch strings.ToLower(strings.TrimSpace(tradeType)) {
	case "app":
		payParams, err = client.PaySignOfApp(appID, rsp.Response.PrepayId)
	case "mini", "miniprogram", "mini_program", "applet":
		payParams, err = client.PaySignOfApplet(appID, rsp.Response.PrepayId)
	default:
		payParams, err = client.PaySignOfJSAPI(appID, rsp.Response.PrepayId)
	}
	if err != nil {
		return nil, fmt.Errorf("生成微信支付 v3 客户端调起参数: %w", err)
	}
	result.Payload = mustJSON(payParams)
	return result, nil
}

func wechatV3APIError(operation string, code int, response gopayWechatV3.ErrResponse, raw string) error {
	message := strings.TrimSpace(response.Message)
	if message == "" {
		message = strings.TrimSpace(raw)
	}
	if message == "" {
		return fmt.Errorf("微信支付 v3 %s HTTP %d", operation, code)
	}
	return fmt.Errorf("微信支付 v3 %s HTTP %d: %s", operation, code, message)
}

func wechatV3ResponsePayload(signInfo *gopayWechatV3.SignInfo, response any) []byte {
	if signInfo != nil && strings.TrimSpace(signInfo.SignBody) != "" {
		return []byte(signInfo.SignBody)
	}
	payload, _ := json.Marshal(response)
	return payload
}

func populateWechatV3Breakdown(result *bizpayment.PaymentResult, object map[string]any) error {
	if result == nil || result.Status != "success" {
		return nil
	}
	amount, ok := valueAtPath(object, "amount").(map[string]any)
	if !ok {
		return nil
	}
	payerTotal, payerOK, err := parseJSONInteger(amount, "payer_total")
	if err != nil {
		return fmt.Errorf("解析微信支付 v3 amount.payer_total: %w", err)
	}
	if !payerOK {
		return nil
	}
	if payerTotal < 0 || payerTotal > result.Amount {
		return errors.New("微信支付 v3 用户实付金额无效")
	}
	result.PayerPaidAmount = payerTotal
	result.CashPaidAmount = payerTotal
	result.DiscountAmount = result.Amount - payerTotal
	if settlement, ok, settlementErr := parseJSONInteger(amount, "settlement_amount"); settlementErr != nil {
		return fmt.Errorf("解析微信支付 v3 amount.settlement_amount: %w", settlementErr)
	} else if ok {
		result.SettlementAmount = settlement
	}
	// promotion_detail contains the authoritative promotion breakdown, but its
	// funding fields vary by promotion type. We only expose the aggregate
	// discount here; the original JSON remains in Payload for audit.
	if result.DiscountAmount < 0 {
		return errors.New("微信支付 v3 优惠金额无效")
	}
	result.AmountBreakdownKnown = true
	return nil
}

func parseJSONInteger(object map[string]any, key string) (int64, bool, error) {
	raw, exists := object[key]
	if !exists || raw == nil || strings.TrimSpace(fmt.Sprint(raw)) == "" || fmt.Sprint(raw) == "<nil>" {
		return 0, false, nil
	}
	amount, err := parseIntegerAmount(fmt.Sprint(raw))
	return amount, true, err
}

func decryptWechatV3(ciphertext, nonce, associatedData, key string) ([]byte, error) {
	if len(key) != 32 {
		return nil, errors.New("微信支付 v3 api_v3_key 必须为 32 字节")
	}
	return gopayWechatV3.V3DecryptNotifyCipherTextToBytes(ciphertext, nonce, associatedData, key)
}
