package utils

import (
	"encoding/json"
	"strings"
	"log"
	"time"
	"fmt"
	"encoding/pem"
	"crypto/x509"
	"crypto/rsa"
	"encoding/base64"
	"errors"
	"io"
	"net/url"
	"hash/crc32"
	crand "crypto/rand"
	"crypto/hmac"
	"crypto/sha1"
	"crypto/md5"
)

// md5
func Md5Sum(text string) string {
	h := md5.New()
	io.WriteString(h, text)
	return fmt.Sprintf("%x", h.Sum(nil))
}

// 加解密函数 根据dz的Authcode改写的go版本
// params[0] 加密or解密 bool true：加密 false：解密 默认false
// params[1] 秘钥
// params[2] 加密：过期时间
// params[3] 动态秘钥长度 默认：4位 不能大于32位
func Authcode(text string, params ...interface{}) string {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("authcode error:%#v", err)
		}
	}()

	l := len(params)

	isEncode := false
	key := "DH-Framework"
	expiry := 0
	cKeyLen := 10

	if l > 0 {
		isEncode = params[0].(bool)
	}

	if l > 1 {
		key = params[1].(string)
	}

	if l > 2 {
		expiry = params[2].(int)
		if expiry < 0 {
			expiry = 0
		}
	}

	if l > 3 {
		cKeyLen = params[3].(int)
		if cKeyLen < 0 {
			cKeyLen = 0
		}
	}
	if cKeyLen > 32 {
		cKeyLen = 32
	}

	timestamp := time.Now().Unix()

	// md5加密key
	mKey := Md5Sum(key)

	// 参与加密的
	keyA := Md5Sum(mKey[0:16])
	// 用于验证数据有效性的
	keyB := Md5Sum(mKey[16:])
	// 动态部分
	var keyC string
	if cKeyLen > 0 {
		if isEncode {
			// 加密的时候，动态获取一个秘钥
			keyC = Md5Sum(fmt.Sprint(timestamp))[32 - cKeyLen:]
		} else {
			// 解密的时候从头部获取动态秘钥部分
			keyC = text[0:cKeyLen]
		}
	}

	// 加入了动态的秘钥
	cryptKey := keyA + Md5Sum(keyA + keyC)
	// 秘钥长度
	keyLen := len(cryptKey)
	if isEncode {
		// 加密 前10位是过期验证字符串 10-26位字符串验证
		var d int64
		if expiry > 0 {
			d = timestamp + int64(expiry)
		}
		text = fmt.Sprintf("%010d%s%s", d, Md5Sum(text + keyB)[0:16], text)
	} else {
		// 解密
		text = string(Base64Decode(text[cKeyLen:]))
	}

	// 字符串长度
	textLen := len(text)
	if textLen <= 0 {
		panic(fmt.Sprintf("auth[%s]textLen<=0", text))
	}

	// 密匙簿
	box := Range(0, 256)
	// 对称算法
	var rndKey []int
	cryptKeyB := []byte(cryptKey)
	for i := 0; i < 256; i++ {
		pos := i % keyLen
		rndKey = append(rndKey, int(cryptKeyB[pos]))
	}

	j := 0
	for i := 0; i < 256; i++ {
		j = (j + box[i] + rndKey[i]) % 256
		box[i], box[j] = box[j], box[i]
	}

	textB := []byte(text)
	a := 0
	j = 0
	var result []byte
	for i := 0; i < textLen; i++ {
		a = (a + 1) % 256
		j = (j + box[a]) % 256
		box[a], box[j] = box[j], box[a]
		result = append(result, byte(int(textB[i]) ^ (box[(box[a] + box[j]) % 256])))
	}

	if isEncode {
		return keyC + strings.Replace(Base64Encode(result), "=", "", -1)
	}

	// 获取前10位，判断过期时间
	d := Atoi64(string(result[0:10]), 0)
	if (d == 0 || d - timestamp > 0) && string(result[10:26]) == Md5Sum(string(result[26:]) + keyB)[0:16] {
		return string(result[26:])
	}

	panic(fmt.Sprintf("auth[%s]", text))

	return ""
}

// AuthcodeUrl 处理Authcode函数的加密解密结果以便url传输
func AuthcodeUrl(text string, params ...interface{}) string {
	isEncode := false
	if len(params) > 0 {
		isEncode = params[0].(bool)
	}
	if isEncode { //加密
		return strings.Replace(strings.Replace(Authcode(text, params...), "+", ",", -1), "/", "-", -1)
	} else {
		return Authcode(strings.Replace(strings.Replace(text, ",", "+", -1), "-", "/", -1), params...)
	}
}

// JsonEncode 编码JSON
func JsonEncode(m interface{}) string {
	b, err := json.Marshal(m)
	if err != nil {
		log.Printf("Json Encode[%#v] Error:%s", m, err.Error())
		return ""
	}
	return string(b)
}

// JsonDecode 解码JSON
func JsonDecode(str string, v ...interface{}) (interface{}, error) {
	var m interface{}
	if len(v) > 0 {
		m = v[0]
	} else {
		m = make(map[string]interface{})
	}

	err := json.Unmarshal([]byte(str), &m)
	if err != nil {
		return nil, err
	}

	return m, nil
}

func Crc32(text string) string {
	h := crc32.NewIEEE()
	io.WriteString(h, text)
	return fmt.Sprintf("%d", h.Sum32())
}

// RsaEncode rsa加密
func RsaEncode(b, rsaKey []byte) ([]byte, error) {
	block, _ := pem.Decode(rsaKey)
	if block == nil {
		return b, errors.New("key error")
	}
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return b, err
	}
	return rsa.EncryptPKCS1v15(crand.Reader, pub.(*rsa.PublicKey), b)
}

// RsaDecode rsa解密
func RsaDecode(b, rsaKey []byte) ([]byte, error) {
	block, _ := pem.Decode(rsaKey)
	if block == nil {
		return b, errors.New("key error")
	}
	priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return b, err
	}
	return rsa.DecryptPKCS1v15(crand.Reader, priv, b)
}

func HashHmac(data, key string) string {
	mac := hmac.New(sha1.New, []byte(key))
	mac.Write([]byte(data))
	return fmt.Sprintf("%x", mac.Sum(nil))
}

func HashHmacRaw(data, key string) string {
	mac := hmac.New(sha1.New, []byte(key))
	mac.Write([]byte(data))
	return fmt.Sprintf("%s", mac.Sum(nil))
}

// Base64Encode Base64编码
func Base64Encode(b []byte) string {
	return base64.StdEncoding.EncodeToString(b)
}

// Base64Decode Base64解码
func Base64Decode(str string) []byte {
	var b []byte
	var err error
	x := len(str) * 3 % 4
	switch {
	case x == 2:
		str += "=="
	case x == 1:
		str += "="
	}
	if b, err = base64.StdEncoding.DecodeString(str); err != nil {
		return b
	}

	return b
}

// UrlEncode 编码
func UrlEncode(str string) string {
	return url.QueryEscape(str)
}

// UrlDecode 解码
func UrlDecode(str string) string {
	ret, _ := url.QueryUnescape(str)
	return ret
}

/*func Urlencode(str string) string {
	return base64.URLEncoding.EncodeToString([]byte(str))
}

func Urldecode(str string) string {
	b, e := base64.URLEncoding.DecodeString(str)
	if e != nil {
		log.Printf("urldecode error:%s", e.Error())
		return ""
	}

	return string(b)
}*/
