package rsa

import (
	config "bwallet/conf"
	"bwallet/pkg/errno"
	"bwallet/pkg/handler"
	"bwallet/pkg/util"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/xinliangnote/go-util/rsa"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

var AppSecret string

// RSA 非对称加密
func Rsa() gin.HandlerFunc {
	return func(c *gin.Context) {
		sign, err := verifySign(c)

		if sign != nil {
			handler.SendResponse(c, nil, sign)
			c.Abort()
			return
		}

		if err != nil {
			handler.SendResponse(c, err, nil)
			c.Abort()
			return
		}

		c.Next()
	}
}

// 验证签名
func verifySign(c *gin.Context) (map[string]string, error) {
	_ = c.Request.ParseForm()
	req := c.Request.Form
	debug := strings.Join(c.Request.Form["debug"], "")
	ak := strings.Join(c.Request.Form["ak"], "")
	sn := strings.Join(c.Request.Form["sn"], "")
	ts := strings.Join(c.Request.Form["ts"], "")

	// 验证来源
	value, ok := config.ApiAuthConfig[ak]
	if ok {
		AppSecret = value["rsa"]
	} else {
		return nil, errno.ErrAk
	}

	if debug == "1" {
		currentUnix := util.GetCurrentUnix()
		req.Set("ts", strconv.FormatInt(currentUnix, 10))

		sn, err := createSign(req)
		if err != nil {
			return nil, errno.ErrSn
		}

		res := map[string]string{
			"ts": strconv.FormatInt(currentUnix, 10),
			"sn": sn,
		}
		return res, nil
	}

	// 验证过期时间
	timestamp := time.Now().Unix()
	exp, _ := strconv.ParseInt(config.AppSignExpiry, 10, 64)
	tsInt, _ := strconv.ParseInt(ts, 10, 64)
	if tsInt > timestamp || timestamp-tsInt >= exp {
		return nil, errno.ErrTs
	}

	// 验证签名
	if sn == "" {
		return nil, errno.ErrSn
	}

	decryptStr, decryptErr := rsa.PrivateDecrypt(sn, config.AppRsaPrivateFile)
	if decryptErr != nil {
		return nil, errors.New(decryptErr.Error())
	}
	if decryptStr != createEncryptStr(req) {
		return nil, errno.ErrSn
	}
	return nil, nil
}

// 创建签名
func createSign(params url.Values) (string, error) {
	return rsa.PublicEncrypt(createEncryptStr(params), AppSecret)
}

func createEncryptStr(params url.Values) string {
	var key []string
	var str = ""
	for k := range params {
		if k != "sn" && k != "debug" {
			key = append(key, k)
		}
	}
	sort.Strings(key)
	for i := 0; i < len(key); i++ {
		if i == 0 {
			str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i]))
		} else {
			str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i]))
		}
	}
	return str
}
