package transfer

import (
	"encoding/xml"
	"fmt"
	"strconv"

	"github.com/silenceper/wechat/v2/pay/config"
	"github.com/silenceper/wechat/v2/util"
)

// 付款到零钱
// https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
var walletTransferGateway = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"

// Transfer struct extends context
type Transfer struct {
	*config.Config
}

// NewTransfer return an instance of Transfer package
func NewTransfer(cfg *config.Config) *Transfer {
	transfer := Transfer{cfg}
	return &transfer
}

// Params 调用参数
type Params struct {
	DeviceInfo     string
	PartnerTradeNo string
	OpenID         string
	CheckName      bool
	ReUserName     string
	Amount         int
	Desc           string
	SpbillCreateIP string
	RootCa         string // ca证书
}

// request 接口请求参数
type request struct {
	AppID          string `xml:"mch_appid"`
	MchID          string `xml:"mchid"`
	NonceStr       string `xml:"nonce_str"`
	Sign           string `xml:"sign"`
	DeviceInfo     string `xml:"device_info,omitempty"`
	PartnerTradeNo string `xml:"partner_trade_no"`
	OpenID         string `xml:"openid"`
	CheckName      string `xml:"check_name"`
	ReUserName     string `xml:"re_user_name,omitempty"`
	Amount         int    `xml:"amount"`
	Desc           string `xml:"desc"`
	SpbillCreateIP string `xml:"spbill_create_ip,omitempty"`
}

// Response 接口返回
type Response struct {
	ReturnCode     string `xml:"return_code"`
	ReturnMsg      string `xml:"return_msg"`
	AppID          string `xml:"appid,omitempty"`
	MchID          string `xml:"mch_id,omitempty"`
	DeviceInfo     string `xml:"device_info,omitempty"`
	NonceStr       string `xml:"nonce_str,omitempty"`
	ResultCode     string `xml:"result_code,omitempty"`
	ErrCode        string `xml:"err_code,omitempty"`
	ErrCodeDes     string `xml:"err_code_des,omitempty"`
	PartnerTradeNo string `xml:"partner_trade_no"`
	PaymentNo      string `xml:"payment_no"`
	PaymentTime    string `xml:"payment_time"`
}

// WalletTransfer 付款到零钱
func (transfer *Transfer) WalletTransfer(p *Params) (rsp Response, err error) {
	nonceStr := util.RandomStr(32)
	param := make(map[string]string)
	param["mch_appid"] = transfer.AppID
	param["mchid"] = transfer.MchID
	param["nonce_str"] = nonceStr
	param["partner_trade_no"] = p.PartnerTradeNo
	param["openid"] = p.OpenID
	param["amount"] = strconv.Itoa(p.Amount)
	param["desc"] = p.Desc
	if p.DeviceInfo != "" {
		param["device_info"] = p.DeviceInfo
	}
	if p.CheckName {
		param["check_name"] = "FORCE_CHECK"
		param["re_user_name"] = p.ReUserName
	} else {
		param["check_name"] = "NO_CHECK"
	}
	if p.SpbillCreateIP != "" {
		param["spbill_create_ip"] = p.SpbillCreateIP
	}

	sign, err := util.ParamSign(param, transfer.Key)
	if err != nil {
		return
	}

	req := request{
		AppID:          transfer.AppID,
		MchID:          transfer.MchID,
		NonceStr:       nonceStr,
		Sign:           sign,
		DeviceInfo:     p.DeviceInfo,
		PartnerTradeNo: p.PartnerTradeNo,
		OpenID:         p.OpenID,
		Amount:         p.Amount,
		Desc:           p.Desc,
		SpbillCreateIP: p.SpbillCreateIP,
	}
	if p.CheckName {
		req.CheckName = "FORCE_CHECK"
		req.ReUserName = p.ReUserName
	} else {
		req.CheckName = "NO_CHECK"
	}

	rawRet, err := util.PostXMLWithTLS(walletTransferGateway, req, p.RootCa, transfer.MchID)
	if err != nil {
		return
	}
	err = xml.Unmarshal(rawRet, &rsp)
	if err != nil {
		return
	}
	if rsp.ReturnCode == "SUCCESS" {
		if rsp.ResultCode == "SUCCESS" {
			err = nil
			return
		}
		err = fmt.Errorf("transfer error, errcode=%s,errmsg=%s", rsp.ErrCode, rsp.ErrCodeDes)
		return
	}
	err = fmt.Errorf("[msg : xmlUnmarshalError] [rawReturn : %s] [sign : %s]", string(rawRet), sign)
	return
}
