package wechat

import (
	"context"
	"crypto/md5"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"go.uber.org/zap"
)

const wechatPayV2UnifiedOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder"

// PaymentV2Service 微信支付 v2 服务
// 适用于仅配置 APIv2Key 的商户（无需 v3 证书序列号）
type PaymentV2Service struct {
	appID     string
	mchID     string
	apiKey    string
	notifyURL string
	logger    *zap.Logger
}

// NewPaymentV2Service 创建微信支付 v2 服务
func NewPaymentV2Service(appID, mchID, apiKey, notifyURL string, logger *zap.Logger) *PaymentV2Service {
	return &PaymentV2Service{
		appID:     appID,
		mchID:     mchID,
		apiKey:    apiKey,
		notifyURL: notifyURL,
		logger:    logger,
	}
}

// v2UnifiedOrderRequest 统一下单请求（v2 XML 格式）
type v2UnifiedOrderRequest struct {
	XMLName    xml.Name `xml:"xml"`
	AppID      string   `xml:"appid"`
	MchID      string   `xml:"mch_id"`
	NonceStr   string   `xml:"nonce_str"`
	Sign       string   `xml:"sign"`
	Body       string   `xml:"body"`
	OutTradeNo string   `xml:"out_trade_no"`
	TotalFee   int      `xml:"total_fee"`
	SpbillIP   string   `xml:"spbill_create_ip"`
	NotifyURL  string   `xml:"notify_url"`
	TradeType  string   `xml:"trade_type"`
	OpenID     string   `xml:"openid"`
}

// v2UnifiedOrderResponse 统一下单响应（v2 XML 格式）
type v2UnifiedOrderResponse struct {
	XMLName    xml.Name `xml:"xml"`
	ReturnCode string   `xml:"return_code"`
	ReturnMsg  string   `xml:"return_msg"`
	ResultCode string   `xml:"result_code"`
	ErrCode    string   `xml:"err_code"`
	ErrCodeDes string   `xml:"err_code_des"`
	PrepayID   string   `xml:"prepay_id"`
}

// CreateJSAPIOrder 创建 v2 JSAPI 支付订单
func (s *PaymentV2Service) CreateJSAPIOrder(ctx context.Context, orderNo, description, openID string, amount int) (*JSAPIPayResult, error) {
	if orderNo == "" || openID == "" || amount <= 0 {
		return nil, errors.New(errors.CodeInvalidParam, "订单号、OpenID 和金额不能为空")
	}

	nonceStr := v2GenerateNonceStr()

	params := map[string]string{
		"appid":            s.appID,
		"mch_id":           s.mchID,
		"nonce_str":        nonceStr,
		"body":             description,
		"out_trade_no":     orderNo,
		"total_fee":        fmt.Sprintf("%d", amount),
		"spbill_create_ip": "127.0.0.1",
		"notify_url":       s.notifyURL,
		"trade_type":       "JSAPI",
		"openid":           openID,
	}
	params["sign"] = v2SignMD5(params, s.apiKey)

	reqBody := &v2UnifiedOrderRequest{
		AppID:      params["appid"],
		MchID:      params["mch_id"],
		NonceStr:   params["nonce_str"],
		Sign:       params["sign"],
		Body:       params["body"],
		OutTradeNo: params["out_trade_no"],
		TotalFee:   amount,
		SpbillIP:   params["spbill_create_ip"],
		NotifyURL:  params["notify_url"],
		TradeType:  params["trade_type"],
		OpenID:     params["openid"],
	}

	xmlBytes, err := xml.Marshal(reqBody)
	if err != nil {
		return nil, errors.Wrap(errors.CodeWechatPayFailed, err, "序列化统一下单请求失败")
	}

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, wechatPayV2UnifiedOrderURL, strings.NewReader(string(xmlBytes)))
	if err != nil {
		return nil, errors.Wrap(errors.CodeWechatPayFailed, err, "构建统一下单请求失败")
	}
	httpReq.Header.Set("Content-Type", "application/xml")

	httpResp, err := http.DefaultClient.Do(httpReq)
	if err != nil {
		s.logger.Error("调用微信 v2 统一下单接口失败", zap.String("order_no", orderNo), zap.Error(err))
		return nil, errors.Wrap(errors.CodeWechatPayFailed, err, "调用统一下单接口失败")
	}
	defer httpResp.Body.Close()

	respBytes, err := io.ReadAll(httpResp.Body)
	if err != nil {
		return nil, errors.Wrap(errors.CodeWechatPayFailed, err, "读取统一下单响应失败")
	}

	var resp v2UnifiedOrderResponse
	if err = xml.Unmarshal(respBytes, &resp); err != nil {
		return nil, errors.Wrap(errors.CodeWechatPayFailed, err, "解析统一下单响应失败")
	}

	if resp.ReturnCode != "SUCCESS" {
		s.logger.Error("微信 v2 统一下单通信失败",
			zap.String("order_no", orderNo),
			zap.String("return_msg", resp.ReturnMsg),
		)
		return nil, errors.New(errors.CodeWechatPayFailed, "统一下单通信失败: "+resp.ReturnMsg)
	}
	if resp.ResultCode != "SUCCESS" {
		s.logger.Error("微信 v2 统一下单业务失败",
			zap.String("order_no", orderNo),
			zap.String("err_code", resp.ErrCode),
			zap.String("err_code_des", resp.ErrCodeDes),
		)
		return nil, errors.New(errors.CodeWechatPayFailed, "统一下单业务失败: "+resp.ErrCodeDes)
	}

	payConfig := s.buildJSAPIPayConfig(resp.PrepayID, nonceStr)

	s.logger.Info("创建 v2 JSAPI 支付订单成功",
		zap.String("order_no", orderNo),
		zap.String("prepay_id", resp.PrepayID),
	)

	return &JSAPIPayResult{
		PrepayID:  resp.PrepayID,
		PayConfig: payConfig,
	}, nil
}

// buildJSAPIPayConfig 构建 JSAPI 唤起支付所需的签名参数
func (s *PaymentV2Service) buildJSAPIPayConfig(prepayID, nonceStr string) map[string]string {
	timestamp := fmt.Sprintf("%d", time.Now().Unix())
	params := map[string]string{
		"appId":     s.appID,
		"timeStamp": timestamp,
		"nonceStr":  nonceStr,
		"package":   "prepay_id=" + prepayID,
		"signType":  "MD5",
	}
	params["paySign"] = v2SignMD5(params, s.apiKey)
	return params
}

// v2SignMD5 对参数按字典序排序后拼接 key，计算 MD5 大写签名
func v2SignMD5(params map[string]string, apiKey string) string {
	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	var sb strings.Builder
	for _, k := range keys {
		v := params[k]
		if v == "" {
			continue
		}
		sb.WriteString(k)
		sb.WriteString("=")
		sb.WriteString(v)
		sb.WriteString("&")
	}
	sb.WriteString("key=")
	sb.WriteString(apiKey)

	hash := md5.Sum([]byte(sb.String()))
	return strings.ToUpper(fmt.Sprintf("%x", hash))
}

func v2GenerateNonceStr() string {
	return fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d", time.Now().UnixNano()))))
}

// v2CallbackNotify v2 支付回调通知 XML 结构
// 所有字段均为 string，避免 CDATA 整数字段的类型转换问题
type v2CallbackNotify struct {
	XMLName       xml.Name `xml:"xml"`
	AppID         string   `xml:"appid"`
	BankType      string   `xml:"bank_type"`
	CashFee       string   `xml:"cash_fee"`
	FeeType       string   `xml:"fee_type"`
	IsSubscribe   string   `xml:"is_subscribe"`
	MchID         string   `xml:"mch_id"`
	NonceStr      string   `xml:"nonce_str"`
	OpenID        string   `xml:"openid"`
	OutTradeNo    string   `xml:"out_trade_no"`
	ResultCode    string   `xml:"result_code"`
	ReturnCode    string   `xml:"return_code"`
	Sign          string   `xml:"sign"`
	TimeEnd       string   `xml:"time_end"`
	TotalFee      string   `xml:"total_fee"`
	TradeType     string   `xml:"trade_type"`
	TransactionID string   `xml:"transaction_id"`
}

// PeekOrderNo 从 v2 回调 XML 中解析订单号（不验签）
// 用于在验签前确定使用哪套支付配置
func PeekOrderNo(body []byte) (string, error) {
	var notify v2CallbackNotify
	if err := xml.Unmarshal(body, &notify); err != nil {
		return "", fmt.Errorf("解析回调 XML 失败: %w", err)
	}
	return notify.OutTradeNo, nil
}

// V2CallbackResult v2 回调解析结果
type V2CallbackResult struct {
	OutTradeNo    string
	TransactionID string
	// TradeState 为 "SUCCESS" 表示支付成功（return_code 和 result_code 均为 SUCCESS）
	TradeState string
	TotalFee   string
	OpenID     string
}

// VerifyCallback 解析并验证 v2 回调 XML，返回解析结果
func (s *PaymentV2Service) VerifyCallback(body []byte) (*V2CallbackResult, error) {
	var notify v2CallbackNotify
	if err := xml.Unmarshal(body, &notify); err != nil {
		return nil, fmt.Errorf("解析回调 XML 失败: %w", err)
	}

	// 构建待签名参数（排除 sign 字段，v2SignMD5 内部自动跳过空值）
	params := map[string]string{
		"appid":          notify.AppID,
		"bank_type":      notify.BankType,
		"cash_fee":       notify.CashFee,
		"fee_type":       notify.FeeType,
		"is_subscribe":   notify.IsSubscribe,
		"mch_id":         notify.MchID,
		"nonce_str":      notify.NonceStr,
		"openid":         notify.OpenID,
		"out_trade_no":   notify.OutTradeNo,
		"result_code":    notify.ResultCode,
		"return_code":    notify.ReturnCode,
		"time_end":       notify.TimeEnd,
		"total_fee":      notify.TotalFee,
		"trade_type":     notify.TradeType,
		"transaction_id": notify.TransactionID,
	}

	expected := v2SignMD5(params, s.apiKey)
	if !strings.EqualFold(notify.Sign, expected) {
		s.logger.Error("v2 回调签名验证失败",
			zap.String("received", notify.Sign),
			zap.String("expected", expected),
			zap.String("mch_id", notify.MchID),
		)
		return nil, fmt.Errorf("签名验证失败")
	}

	// return_code 和 result_code 均为 SUCCESS 才算支付成功
	tradeState := ""
	if notify.ReturnCode == "SUCCESS" && notify.ResultCode == "SUCCESS" {
		tradeState = "SUCCESS"
	}

	return &V2CallbackResult{
		OutTradeNo:    notify.OutTradeNo,
		TransactionID: notify.TransactionID,
		TradeState:    tradeState,
		TotalFee:      notify.TotalFee,
		OpenID:        notify.OpenID,
	}, nil
}
