package wechat

import (
	"crypto/rsa"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/pkg/xlog"
)

type Resource struct {
	OriginalType   string `json:"original_type"`
	Algorithm      string `json:"algorithm"`
	Ciphertext     string `json:"ciphertext"`
	AssociatedData string `json:"associated_data"`
	Nonce          string `json:"nonce"`
}

type V3DecryptResult struct {
	Appid           string             `json:"appid"`
	Mchid           string             `json:"mchid"`
	OutTradeNo      string             `json:"out_trade_no"`
	TransactionId   string             `json:"transaction_id"`
	TradeType       string             `json:"trade_type"`
	TradeState      string             `json:"trade_state"`
	TradeStateDesc  string             `json:"trade_state_desc"`
	BankType        string             `json:"bank_type"`
	Attach          string             `json:"attach"`
	SuccessTime     string             `json:"success_time"`
	Payer           *Payer             `json:"payer"`
	Amount          *Amount            `json:"amount"`
	SceneInfo       *SceneInfo         `json:"scene_info"`
	PromotionDetail []*PromotionDetail `json:"promotion_detail"`
}

type V3DecryptPartnerResult struct {
	SpAppid         string             `json:"sp_appid"`
	SpMchid         string             `json:"sp_mchid"`
	SubAppid        string             `json:"sub_appid"`
	SubMchid        string             `json:"sub_mchid"`
	OutTradeNo      string             `json:"out_trade_no"`
	TransactionId   string             `json:"transaction_id"`
	TradeType       string             `json:"trade_type"`
	TradeState      string             `json:"trade_state"`
	TradeStateDesc  string             `json:"trade_state_desc"`
	BankType        string             `json:"bank_type"`
	Attach          string             `json:"attach"`
	SuccessTime     string             `json:"success_time"`
	Payer           *PartnerPayer      `json:"payer"`
	Amount          *Amount            `json:"amount"`
	SceneInfo       *SceneInfo         `json:"scene_info"`
	PromotionDetail []*PromotionDetail `json:"promotion_detail"`
}

type V3DecryptRefundResult struct {
	Mchid               string        `json:"mchid"`
	OutTradeNo          string        `json:"out_trade_no"`
	TransactionId       string        `json:"transaction_id"`
	OutRefundNo         string        `json:"out_refund_no"`
	RefundId            string        `json:"refund_id"`
	RefundStatus        string        `json:"refund_status"`
	SuccessTime         string        `json:"success_time"`
	UserReceivedAccount string        `json:"user_received_account"`
	Amount              *RefundAmount `json:"amount"`
}

type V3DecryptPartnerRefundResult struct {
	SpMchid             string        `json:"sp_mchid"`
	SubMchid            string        `json:"sub_mchid"`
	OutTradeNo          string        `json:"out_trade_no"`
	TransactionId       string        `json:"transaction_id"`
	OutRefundNo         string        `json:"out_refund_no"`
	RefundId            string        `json:"refund_id"`
	RefundStatus        string        `json:"refund_status"`
	SuccessTime         string        `json:"success_time"`
	UserReceivedAccount string        `json:"user_received_account"`
	Amount              *RefundAmount `json:"amount"`
}

type V3DecryptCombineResult struct {
	CombineAppid      string       `json:"combine_appid"`
	CombineMchid      string       `json:"combine_mchid"`
	CombineOutTradeNo string       `json:"combine_out_trade_no"`
	SceneInfo         *SceneInfo   `json:"scene_info"`
	SubOrders         []*SubOrders `json:"sub_orders"`         // 最多支持子单条数：50
	CombinePayerInfo  *Payer       `json:"combine_payer_info"` // 支付者信息
}

type V3DecryptScoreResult struct {
	Appid               string           `json:"appid"`
	Mchid               string           `json:"mchid"`
	OutOrderNo          string           `json:"out_order_no"`
	ServiceId           string           `json:"service_id"`
	Openid              string           `json:"openid"`
	State               string           `json:"state"`
	StateDescription    string           `json:"state_description"`
	TotalAmount         int              `json:"total_amount"`
	ServiceIntroduction string           `json:"service_introduction"`
	PostPayments        []*PostPayments  `json:"post_payments"`
	PostDiscounts       []*PostDiscounts `json:"post_discounts"`
	RiskFund            *RiskFund        `json:"risk_fund"`
	TimeRange           *TimeRange       `json:"time_range"`
	Location            *Location        `json:"location"`
	Attach              string           `json:"attach"`
	NotifyUrl           string           `json:"notify_url"`
	OrderId             string           `json:"order_id"`
	NeedCollection      bool             `json:"need_collection"`
	Collection          *Collection      `json:"collection"`
}

type V3DecryptProfitShareResult struct {
	SpMchid       string    `json:"sp_mchid"`       // 服务商商户号
	SubMchid      string    `json:"sub_mchid"`      // 子商户号
	TransactionId string    `json:"transaction_id"` // 微信订单号
	OrderId       string    `json:"order_id"`       // 微信分账/回退单号
	OutOrderNo    string    `json:"out_order_no"`   // 商户分账/回退单号
	Receiver      *Receiver `json:"receiver"`
	SuccessTime   string    `json:"success_time"` // 成功时间
}

type Receiver struct {
	Type        string `json:"type"`        // 分账接收方类型
	Account     string `json:"account"`     // 分账接收方账号
	Amount      int    `json:"amount"`      // 分账动账金额
	Description string `json:"description"` // 分账/回退描述
}

type V3DecryptBusifavorResult struct {
	EventType    string               `json:"event_type"`    // 事件类型
	CouponCode   string               `json:"coupon_code"`   // 券code
	StockId      string               `json:"stock_id"`      // 批次号
	SendTime     string               `json:"send_time"`     // 发放时间
	Openid       string               `json:"openid"`        // 用户标识
	Unionid      string               `json:"unionid"`       // 用户统一标识
	SendChannel  string               `json:"send_channel"`  // 发放渠道
	SendMerchant string               `json:"send_merchant"` // 发券商户号
	AttachInfo   *BusifavorAttachInfo `json:"attach_info"`   // 发券附加信息
}

type BusifavorAttachInfo struct {
	TransactionId   string `json:"transaction_id"`     // 交易订单编号
	ActCode         string `json:"act_code"`           // 支付有礼活动编号/营销馆活动ID
	HallCode        string `json:"hall_code"`          // 营销馆ID
	HallBelongMchID int    `json:"hall_belong_mch_id"` // 营销馆所属商户号
	CardID          string `json:"card_id"`            // 会员卡ID
	Code            string `json:"code"`               // 会员卡code
	ActivityID      string `json:"activity_id"`        // 会员活动ID
}

type V3NotifyReq struct {
	Id           string    `json:"id"`
	CreateTime   string    `json:"create_time"`
	ResourceType string    `json:"resource_type"`
	EventType    string    `json:"event_type"`
	Summary      string    `json:"summary"`
	Resource     *Resource `json:"resource"`
	SignInfo     *SignInfo `json:"-"`
}

type V3NotifyRsp struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

// 解析微信回调请求的参数到 V3NotifyReq 结构体
func V3ParseNotify(req *http.Request) (notifyReq *V3NotifyReq, err error) {
	bs, err := ioutil.ReadAll(io.LimitReader(req.Body, int64(3<<20))) // default 3MB change the size you want;
	defer req.Body.Close()
	if err != nil {
		return nil, fmt.Errorf("read request body error:%w", err)
	}
	si := &SignInfo{
		HeaderTimestamp: req.Header.Get(HeaderTimestamp),
		HeaderNonce:     req.Header.Get(HeaderNonce),
		HeaderSignature: req.Header.Get(HeaderSignature),
		HeaderSerial:    req.Header.Get(HeaderSerial),
		SignBody:        string(bs),
	}
	notifyReq = &V3NotifyReq{SignInfo: si}
	if err = json.Unmarshal(bs, notifyReq); err != nil {
		return nil, fmt.Errorf("json.Unmarshal(%s, %+v)：%w", string(bs), notifyReq, err)
	}
	return notifyReq, nil
}

// Deprecated
//	推荐使用 VerifySignByPK()
func (v *V3NotifyReq) VerifySign(wxPkContent string) (err error) {
	if v.SignInfo != nil {
		return V3VerifySign(v.SignInfo.HeaderTimestamp, v.SignInfo.HeaderNonce, v.SignInfo.SignBody, v.SignInfo.HeaderSignature, wxPkContent)
	}
	return errors.New("verify notify sign, bug SignInfo is nil")
}

// 异步通知验签
//	wxPublicKey：微信平台证书公钥内容，通过 client.WxPublicKey() 获取
func (v *V3NotifyReq) VerifySignByPK(wxPublicKey *rsa.PublicKey) (err error) {
	if v.SignInfo != nil {
		return V3VerifySignByPK(v.SignInfo.HeaderTimestamp, v.SignInfo.HeaderNonce, v.SignInfo.SignBody, v.SignInfo.HeaderSignature, wxPublicKey)
	}
	return errors.New("verify notify sign, bug SignInfo is nil")
}

// 解密 普通支付 回调中的加密信息
func (v *V3NotifyReq) DecryptCipherText(apiV3Key string) (result *V3DecryptResult, err error) {
	if v.Resource != nil {
		result, err = V3DecryptNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
		if err != nil {
			bytes, _ := json.Marshal(v)
			return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
		}
		return result, nil
	}
	return nil, errors.New("notify data Resource is nil")
}

// 解密 服务商支付 回调中的加密信息
func (v *V3NotifyReq) DecryptPartnerCipherText(apiV3Key string) (result *V3DecryptPartnerResult, err error) {
	if v.Resource != nil {
		result, err = V3DecryptPartnerNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
		if err != nil {
			bytes, _ := json.Marshal(v)
			return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
		}
		return result, nil
	}
	return nil, errors.New("notify data Resource is nil")
}

// 解密 普通退款 回调中的加密信息
func (v *V3NotifyReq) DecryptRefundCipherText(apiV3Key string) (result *V3DecryptRefundResult, err error) {
	if v.Resource != nil {
		result, err = V3DecryptRefundNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
		if err != nil {
			bytes, _ := json.Marshal(v)
			return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
		}
		return result, nil
	}
	return nil, errors.New("notify data Resource is nil")
}

// 解密 服务商退款 回调中的加密信息
func (v *V3NotifyReq) DecryptPartnerRefundCipherText(apiV3Key string) (result *V3DecryptPartnerRefundResult, err error) {
	if v.Resource != nil {
		result, err = V3DecryptPartnerRefundNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
		if err != nil {
			bytes, _ := json.Marshal(v)
			return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
		}
		return result, nil
	}
	return nil, errors.New("notify data Resource is nil")
}

// 解密 合单支付 回调中的加密信息
func (v *V3NotifyReq) DecryptCombineCipherText(apiV3Key string) (result *V3DecryptCombineResult, err error) {
	if v.Resource != nil {
		result, err = V3DecryptCombineNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
		if err != nil {
			bytes, _ := json.Marshal(v)
			return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
		}
		return result, nil
	}
	return nil, errors.New("notify data Resource is nil")
}

// 解密 支付分 回调中的加密信息
func (v *V3NotifyReq) DecryptScoreCipherText(apiV3Key string) (result *V3DecryptScoreResult, err error) {
	if v.Resource != nil {
		result, err = V3DecryptScoreNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
		if err != nil {
			bytes, _ := json.Marshal(v)
			return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
		}
		return result, nil
	}
	return nil, errors.New("notify data Resource is nil")
}

// 解密分账动账回调中的加密信息
func (v *V3NotifyReq) DecryptProfitShareCipherText(apiV3Key string) (result *V3DecryptProfitShareResult, err error) {
	if v.Resource != nil {
		result, err = V3DecryptProfitShareNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
		if err != nil {
			bytes, _ := json.Marshal(v)
			return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
		}
		return result, nil
	}
	return nil, errors.New("notify data Resource is nil")
}

// 解密商家券回调中的加密信息
func (v *V3NotifyReq) DecryptBusifavorCipherText(apiV3Key string) (result *V3DecryptBusifavorResult, err error) {
	if v.Resource != nil {
		result, err = V3DecryptBusifavorNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
		if err != nil {
			bytes, _ := json.Marshal(v)
			return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
		}
		return result, nil
	}
	return nil, errors.New("notify data Resource is nil")
}

// Deprecated
//	暂时不推荐此方法，请使用 wechat.V3ParseNotify()
//	解析微信回调请求的参数到 gopay.BodyMap
func V3ParseNotifyToBodyMap(req *http.Request) (bm gopay.BodyMap, err error) {
	bs, err := ioutil.ReadAll(io.LimitReader(req.Body, int64(3<<20))) // default 3MB change the size you want;
	defer req.Body.Close()
	if err != nil {
		xlog.Error("err:", err)
		return
	}
	bm = make(gopay.BodyMap)
	if err = json.Unmarshal(bs, &bm); err != nil {
		return nil, fmt.Errorf("json.Unmarshal(%s)：%w", string(bs), err)
	}
	return bm, nil
}
