package udunhelper

import (
	"crypto/md5"
	"crypto/tls"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	log "github.com/go-admin-team/go-admin-core/logger"
	ext "go-admin/config"
	"go-admin/pkg/jsonhelper"
	"go-admin/pkg/utility/seqs"
	"io"
	"net/http"
	"strings"
	"time"

	"go.uber.org/zap"
)

const (
	createAddressUrl = "/mch/address/create"
	withdrawUrl      = "/mch/withdraw"
	checkAddressUrl  = "/mch/check/address"
	supportCoinsUrl  = "/mch/support-coins"
	existAddressUrl  = "/mch/exist/address"

	Notify = "/api/v1/line/notify"
)

var (
	clientUDun *http.Client
)

func init() {
	t := http.DefaultTransport.(*http.Transport).Clone()
	t.MaxIdleConns = 100
	t.MaxConnsPerHost = 10000
	t.MaxIdleConnsPerHost = 100
	t.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

	//u盾client
	clientUDun = &http.Client{
		Timeout:   50 * time.Second, //time.Duration(timeout) * time.Second,
		Transport: t,
	}
	//UdKey = config.AppGlobalConfig.UDunKey                                 // "d333ae13beb3a96c0225847098267cf3"
	//UdMerchantId = config.AppGlobalConfig.UDunMerchantID                   // "311129"
	//CallUrl = config.AppGlobalConfig.CurrServerIp + "/api/recharge/notify" // "https://8.218.110.85/api/recharge/notify" // 回调地址
	//baseUrl = config.AppGlobalConfig.UDunUrl                               //"https://sig10.udun.io"
}

/* 回调例子
"timestamp": 1535005047,
    "nonce": 100000,
    "sign": "e1bee3a417b9c606ba6cedda26db761a",
    "body": "{\"address\":\"DJY781Z8qbuJeuA7C3McYivbX8kmAUXPsW\",\"amount\":\"12345678\",\"blockHigh\":\"102419\",\"coinType\":\"206\",\"decimals\":\"8\",\"fee\":\"452000\",
\"mainCoinType\":\"206\",\"status\":3,\"tradeId\":\"20181024175416907\",\"tradeType\":1,\"txId\":\"31689c332536b56a2246347e206fbed2d04d461a3d668c4c1de32a75a8d436f0\"}"
*/

// GetSupportCoinsByMerchant 获取商家支持币种
func GetSupportCoinsByMerchant(baseReq BaseRequest, showBalance bool) BaseCoinsMerchant {
	mp := make(map[string]interface{}, 0)
	//mp["merchantId"] = config.AppGlobalConfig.UDunMerchantID
	mp["merchantId"] = ext.ExtConfig.UDunConfig.UDunMerchantID
	mp["showBalance"] = showBalance
	reqBody, _ := json.Marshal(mp)
	sign := Signature(baseReq.Nonce, baseReq.Timestamp, string(reqBody))
	url := fmt.Sprintf("%v%v", ext.ExtConfig.UDunConfig.UDunUrl, supportCoinsUrl)
	re, err := sendRequestUDun(url, baseReq.Nonce, baseReq.Timestamp, string(reqBody), sign)
	if err != nil {
		log.Error("GetSupportCoinsByMerchant", zap.Error(err))
		return BaseCoinsMerchant{}
	}

	var res BaseCoinsMerchant
	err = jsonhelper.Unmarshal(re, &res)
	if err != nil {
		return BaseCoinsMerchant{}
	}
	return res
}

// CreateAddress 创建地址
func CreateAddress(baseReq BaseRequest, callback []*CallbackRequest) BaseCoinAddress {
	for _, c := range callback {
		c.MerchantId = ext.ExtConfig.UDunConfig.UDunMerchantID
		c.CallUrl = ext.ExtConfig.UDunConfig.CurrServerIp + Notify // "/api/recharge/notify"
	}
	reqBody, _ := json.Marshal(callback)
	sign := Signature(baseReq.Nonce, baseReq.Timestamp, string(reqBody))
	url := fmt.Sprintf("%v%v", ext.ExtConfig.UDunConfig.UDunUrl, createAddressUrl)
	re, err := sendRequestUDun(url, baseReq.Nonce, baseReq.Timestamp, string(reqBody), sign)
	if err != nil {
		log.Error("CreateAddress", zap.Error(err), zap.String("url", url))
		return BaseCoinAddress{}
	}
	var res BaseCoinAddress
	err = jsonhelper.Unmarshal(re, &res)
	if err != nil {
		log.Error("CreateAddress Unmarshal", zap.Error(err))
		return BaseCoinAddress{}
	}
	return res
}

// Withdraw 提现
func Withdraw(withdraw []*WithdrawRequest) (code int, msg string) {
	timeStamp := time.Now().Unix()
	nonce := seqs.Rand().RandInt(600000)
	// 序列化
	reqBody, _ := json.Marshal(withdraw)
	// 签名
	sign := Signature(nonce, timeStamp, string(reqBody))
	url := fmt.Sprintf("%v%v", ext.ExtConfig.UDunConfig.UDunUrl, withdrawUrl)
	// 提交
	re, err := sendRequestUDun(url, nonce, timeStamp, string(reqBody), sign)
	if err != nil {
		log.Error("Withdraw", zap.Error(err))
		return 0, "调用失败"
	}
	// 反序列化
	var res BaseRes
	if err := jsonhelper.Unmarshal(re, &res); err != nil {
		return 0, "Withdraw jsonhelper.Unmarshal失败"
	}
	return res.Code, getWithDrawMsg(res.Code) //res.Message
}

// CheckAddress 检查地址是否正确
func CheckAddress(baseReq BaseRequest, verify []*VerifyRequest) (code int, msg string) {
	reqBody, _ := json.Marshal(verify)
	sign := Signature(baseReq.Nonce, baseReq.Timestamp, string(reqBody))
	url := fmt.Sprintf("%v%v", ext.ExtConfig.UDunConfig.UDunUrl, checkAddressUrl)
	re, err := sendRequestUDun(url, baseReq.Nonce, baseReq.Timestamp, string(reqBody), sign)
	if err != nil {
		log.Error("CheckAddress", zap.Error(err))
		return 0, "调用失败"
	}
	var res BaseRes
	err = jsonhelper.Unmarshal(re, &res)
	if err != nil {
		return 0, "jsonhelper.Unmarshal失败"
	}
	return res.Code, res.Message
}

// ExistAddress 检查地址是否在udun存在
func ExistAddress(baseReq BaseRequest, verify []*VerifyRequest) (code int, msg string) {
	reqBody, _ := json.Marshal(verify)
	sign := Signature(baseReq.Nonce, baseReq.Timestamp, string(reqBody))
	url := fmt.Sprintf("%v%v", ext.ExtConfig.UDunConfig.UDunUrl, existAddressUrl)
	re, err := sendRequestUDun(url, baseReq.Nonce, baseReq.Timestamp, string(reqBody), sign)
	if err != nil {
		log.Error("ExistAddress", zap.Error(err))
		return 0, "调用失败"
	}
	var res BaseRes
	err = jsonhelper.Unmarshal(re, &res)
	if err != nil {
		return 0, "jsonhelper.Unmarshal失败"
	}
	return res.Code, res.Message
}

func Signature(nonce int, timestamp int64, body string) string {
	msg := fmt.Sprintf("%v%v%v%v", body, ext.ExtConfig.UDunConfig.UDunKey, nonce, timestamp)
	h := md5.New()
	h.Write([]byte(msg))
	return hex.EncodeToString(h.Sum(nil))
}

func CheckCallBackSign(timestamp string, nonce string, body string) string {
	raw := body + ext.ExtConfig.UDunConfig.UDunKey + nonce + timestamp
	h := md5.New()
	h.Write([]byte(raw))
	return hex.EncodeToString(h.Sum(nil))
}

func sendRequestUDun(url string, nonce int, timestamp int64, reqBody string, sign string) ([]byte, error) {
	q := HttpRequest{Nonce: nonce, Timestamp: timestamp, Sign: sign, Body: reqBody}
	body, _ := json.Marshal(q)
	headers := map[string]string{}
	headers["Content-Type"] = "application/json"
	resp, err := newHttpRequest(url, string(body), headers)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func newHttpRequest(reqUrl, postData string, reqHeaders map[string]string) ([]byte, error) {
	req, _ := http.NewRequest("POST", reqUrl, strings.NewReader(postData))
	if req.Header.Get("User-Agent") == "" {
		req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36")
	}
	if reqHeaders != nil {
		for k, v := range reqHeaders {
			req.Header.Add(k, v)
		}
	}

	resp, err := clientUDun.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	bodyData, err := io.ReadAll(resp.Body) // ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	arr := make([]byte, len(bodyData))
	copy(arr, bodyData)

	if resp.StatusCode != 200 {
		return nil, errors.New(string(arr))
	}

	return arr, nil
}

/*
createAddressUrl 创建地址 接口返回code说明
-1	生成地址失敗
200	生成地址成功
4001	商户不存在
4005	非法參數
4045	幣種信息錯誤
4162	簽名異常
4163	簽名錯誤
4166	商戶沒有配置套餐
4168	商戶地址達到上限
4169	商戶已禁用
4175	錢包編號錯誤
4017	商戶沒有創建錢包
4176	錢包未添加支持該幣種
4188	暫不支持
4226	商戶普通賬戶被禁用
4261	商戶管理員賬戶被禁用
4262	賬戶不存在

withdrawUrl 提币 接口返回code说明
200	提幣成功
523	參數為空
581	無效的提幣金額
4005	非法參數
4014	幣種為空
4034	未找到該幣種信息
4162	簽名異常
4163	簽名錯誤
4169	商戶已被禁用
4183	到賬地址異常
4193	EOS金額小數點後超過4位長度
4214	暫無可用的幣種
4226	商戶普通賬戶被禁用
4261	商戶管理員賬戶被禁用
4284	商户不存在
4288	業務編號(BusinessId)重復,請勿重復申請
4598	傳入body中的list對象中的所有merchantId必須保持一致
4001	商户不存在
*/

//分充值回調和提幣回調， 其中提幣最多會進行兩次回調（ 審核回調 + 交易結果回調）

//func SendRequest(url string, nonce int, timestamp int64, reqBody string, sign string) (code int64, msg string, extra string) {
//	q := HttpRequest{Nonce: nonce, Timestamp: timestamp, Sign: sign, Body: reqBody}
//	body, _ := json.Marshal(q)
//	resp, err := clientUDun.Post(url, "application/json", bytes.NewBuffer(body))
//	if err != nil {
//		return http.StatusBadRequest, err.Error(), ""
//	}
//	defer resp.Body.Close()
//	r, err := ioutil.ReadAll(resp.Body)
//	if err != nil {
//		return http.StatusBadGateway, err.Error(), ""
//	}
//
//	js := gjson.ParseBytes(r)
//	code = js.Get("code").Int()
//	msg = js.Get("message").String()
//	if js.Get("data").Exists() {
//		extra = js.Get("data").String()
//	}
//	return
//}

// CallBackFun 網關收到交易處理結果，調用商戶提供的回調接口，通知商戶具體變化信息。該接口網關發送給您指定的回調地址的內容，處理您的業務信息。
// 分充值回調和提幣回調， 其中提幣最多會進行兩次回調（ 審核回調 + 交易結果回調）
func CallBackFun(res CallBackBase) {

}
