package common

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha256"
	"designs/config"
	"designs/global"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
)

// 打印
func Print(i interface{}) {
	fmt.Println("---")
	fmt.Println(i)
	fmt.Println("---")
}

// 返回JSON
func RetJson(code int, msg string, data interface{}, c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"code":      code,
		"msg":       msg,
		"data":      data,
		"timestamp": GetTimeUnix(),
	})
	c.Abort()
}

// 获取当前时间戳
func GetTimeUnix() int64 {
	return time.Now().Unix()
}

// MD5 方法
func MD5(str string) string {
	s := md5.New()
	s.Write([]byte(str))
	return hex.EncodeToString(s.Sum(nil))
}

// 生成签名
func CreateSign(params url.Values) string {
	var key []string
	var str = ""
	for k := range params {
		if k != "sn" {
			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]))
		}
	}
	// 自定义签名算法
	sign := MD5(MD5(str) + MD5(config.Get("app_name")+config.Get("app_secret")))
	return sign
}

// 验证签名
func VerifySign(c *gin.Context) {
	var method = c.Request.Method
	var ts int64
	var sn string
	var req url.Values

	if method == "GET" {
		req = c.Request.URL.Query()
		sn = c.Query("sn")
		ts, _ = strconv.ParseInt(c.Query("ts"), 10, 64)

	} else if method == "POST" {
		c.Request.ParseForm()
		req = c.Request.PostForm
		sn = c.PostForm("sn")
		ts, _ = strconv.ParseInt(c.PostForm("ts"), 10, 64)
	} else {
		RetJson(500, "Illegal requests", "", c)
		return
	}

	exp, _ := strconv.ParseInt(config.Get("api_expire"), 10, 64)

	// 验证过期时间
	if ts > GetTimeUnix() || GetTimeUnix()-ts >= exp {
		RetJson(500, "Ts Error", "", c)
		return
	}

	// 验证签名
	if sn == "" || sn != CreateSign(req) {
		RetJson(500, "Sn Error", "", c)
		return
	}
}

func StructToArr(data interface{}) (string, error) {
	res, err := json.Marshal(data)
	if err != nil {
		return "", err
	}
	//将json解析为map
	var mapdata map[string]interface{}
	err1 := json.Unmarshal([]byte(res), &mapdata)
	if err1 != nil {
		return "", err1
	}
	//获取排序map键值
	keys := make([]string, 0, len(mapdata))
	for key := range mapdata {
		keys = append(keys, key)
	}
	//排序
	sort.Strings(keys)
	fmt.Println("我的数据:", string(res))
	//排序后直接构建map
	sortMap := make(map[string]interface{}, len(mapdata))
	for _, key := range keys {
		sortMap[key] = mapdata[key]
	}
	//序列化
	sortJosn, err2 := json.Marshal(sortMap)
	if err2 != nil {
		return "", err2
	}
	return string(sortJosn), nil
}

/* 检测数据是否篡改 */
func VerifySignData(data interface{}) bool {
	var checkSign string
	var sign string = "test"
	checkKey := "secret"

	res, err := json.Marshal(data)
	if err != nil {
		return false
	}
	//将json解析为map
	var mapdata map[string]interface{}
	err1 := json.Unmarshal([]byte(res), &mapdata)
	if err1 != nil {
		return false
	}
	//fmt.Println("解析后的数据:", mapdata)

	val, exist := mapdata[checkKey].(string)
	if exist {
		checkSign = val
	} else {
		return false
	}
	fmt.Println("解析后的数据:", mapdata)
	//获取排序map键值
	keys := make([]string, 0, len(mapdata))
	for key := range mapdata {
		if key != checkKey {
			keys = append(keys, key)
		}
	}
	// fmt.Println("解析后的keys数据:", keys)
	//排序
	sort.Strings(keys)
	//排序后直接构建map
	sortMap := make(map[string]interface{}, len(mapdata))
	for _, key := range keys {
		newKey := strings.ToLower(string(key[0])) + key[1:]
		sortMap[newKey] = mapdata[key]
	}
	//序列化数据
	sortJosn, err2 := json.Marshal(sortMap)
	if err2 != nil {
		return false
	}
	// fmt.Println("我的排序数据:", string(sortJosn), sign)

	// sum :=  ([]byte("hello world"))
	// fmt.Printf("加密数据:%x\n", sum)
	// 密钥
	key := []byte(config.Get("app.app_check_secret"))
	// 数据
	// data := []byte(msg)
	// 创建一个新的HMAC将SHA-256用作哈希函数
	h := hmac.New(sha256.New, key)
	// 写入数据
	h.Write(sortJosn)
	// 得到MAC
	mac := h.Sum(nil)
	sign = hex.EncodeToString(mac)
	// 打印MAC
	fmt.Println("生成的签名:", sign, checkSign)

	if sign != checkSign {
		global.App.Log.Error("mapdata:", mapdata)
		global.App.Log.Error("sign:", sign)
		global.App.Log.Error("checkSign:", checkSign)
	}

	return sign == checkSign
}

// 生成token
func GetDefineToken(openid string) string {
	timestamp := time.Now().UnixNano() / int64(time.Millisecond)
	randNum := 10000 + rand.Intn(10000)
	token := fmt.Sprintf("%s%d%d", openid, timestamp, randNum)
	return MD5(token)
}

// 自定义结构体
type MyCustomClaims struct {
	UserName  string `json:"userName"`
	TokenType int    `json:"tokenType"` //0 token 登录  1：刷新token
	jwt.RegisteredClaims
}

// 生成jwt token
func GetJwtToken(UserName string, tokenType int) (string, error) {
	mySigningKey := []byte(config.Get("app.JwtSecret"))
	timeData := time.Second
	if tokenType == 0 {
		timeData = time.Duration(config.GetInt("app.api_exp")) * time.Second
	} else {
		timeData = 24 * time.Hour
	}
	claims := MyCustomClaims{
		UserName,
		tokenType,
		jwt.RegisteredClaims{
			// A usual scenario is to set the expiration time relative to the current time
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(timeData)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			NotBefore: jwt.NewNumericDate(time.Now()),
			Issuer:    "test",
			Subject:   "somebody",
			ID:        "1",
			Audience:  []string{"somebody_else"},
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	ss, err := token.SignedString(mySigningKey)
	return ss, err
}

/* 解析jwt */
func ParseJwtWithClaims(tokenString string) (string, int) {
	token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(config.Get("app.JwtSecret")), nil
	})
	if err != nil {
		// fmt.Println(err)
		return "", 0
	} else if claims, ok := token.Claims.(*MyCustomClaims); ok {
		// fmt.Println(claims.OpenId, claims.RegisteredClaims.Issuer)
		return claims.UserName, claims.TokenType
	} else {
		// fmt.Println("unknown claims type, cannot proceed")
		return "", 0
	}
}

// 刷新Token
func RefreshToken(oldToken string) (string, error) {
	token, err := jwt.Parse(oldToken, func(*jwt.Token) (interface{}, error) {
		return config.Get("app.JwtSecret"), nil
	})
	if err != nil {
		return "", err
	}
	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		claims["exp"] = time.Now().Add(time.Duration(config.GetInt("app.api_exp")) * time.Second).Unix() // 更新过期时间
		return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(config.Get("app.JwtSecret"))
	}
	return "", err
}

// 返回剩余时间
func GetRemainderTime(timeType int) (time.Duration, error) {
	var remaining time.Duration
	err := errors.New("发生错误")
	//获取当前时间
	now := time.Now()
	switch timeType {
	case 2:
		//计算本日剩余时间
		endOfDay := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location())
		remaining = endOfDay.Sub(now)
	case 3:
		//计算本周剩余时间
		daysUntiEndOfWeek := 6 - now.Weekday()
		endOfWeek := time.Date(now.Year(), now.Month(), now.Day()+int(daysUntiEndOfWeek), 23, 59, 59, 0, now.Location())
		remaining = endOfWeek.Sub(now)
	case 4:
		//计算本月剩余时间
		// endOfMonth := time.Date(now.Year(), now.Month()+1, 1, 0, 0, 0, 0, now.Location()).Add(-time.Second)
		// remaining = endOfMonth.Sub(now)
		nextMonth := now.AddDate(0, 1, 0)
		endOfMonth := time.Date(nextMonth.Year(), nextMonth.Month(), 1, 0, 0, 0, 0, nextMonth.Location()).Add(-time.Second)
		remaining = endOfMonth.Sub(now)
	default:
		return remaining, err
	}

	// fmt.Printf("Remaning day data:%v\n", remaining)
	// fmt.Printf("Remaning week data:%v\n", remaining)
	// fmt.Printf("Remaning month data:%v\n", remaining)
	return remaining, nil
}
