package oauth

import (
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"math/rand"
	"net/http"
	"strings"
	"time"

	"be.ems/lib/log"

	"github.com/dgrijalva/jwt-go"
	"golang.org/x/crypto/bcrypt"
)

// GenToken 生成Token值
func GenToken(mapClaims jwt.MapClaims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)
	var nowDate = time.Now()
	var secret = fmt.Sprintf("%v%v", nowDate, "xxxx")
	return token.SignedString([]byte(secret))
}

// GenerateToken 生成Token值
func GenerateToken(mapClaims jwt.MapClaims, key string) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)
	return token.SignedString([]byte(key))
}

// ParseToken: "解析token"
func ParseToken(token string, secret string) (string, error) {
	claim, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
		return []byte(secret), nil
	})
	if err != nil {
		return "", err
	}
	return claim.Claims.(jwt.MapClaims)["cmd"].(string), nil
}

func RandAccessToken(n int) (ret string) {
	allString := "52661fbd-6b84-4fc2-aa1e-17879a5c6c9b"
	ret = ""
	for i := 0; i < n; i++ {
		r := rand.Intn(len(allString))
		ret = ret + allString[r:r+1]
	}
	return ret
}

const letterBytes = "abcdef0123456789"
const (
	letterIdxBits = 6                    // 6 bits to represent a letter index
	letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
	letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
)

var src = rand.NewSource(time.Now().UnixNano())

func RandStringBytes(n int) string {
	b := make([]byte, n)
	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache >>= letterIdxBits
		remain--
	}

	return string(b)
}

func GenRandToken(prefix string) string {
	if prefix == "" {
		return RandStringBytes(8) + "-" + RandStringBytes(4) + "-" +
			RandStringBytes(4) + "-" + RandStringBytes(4) + "-" + RandStringBytes(12)
	} else {
		return prefix + "-" + RandStringBytes(8) + "-" + RandStringBytes(4) + "-" +
			RandStringBytes(4) + "-" + RandStringBytes(4) + "-" + RandStringBytes(12)
	}
}

type OAuthBody struct {
	GrantType string
	UserName  string
	Value     string
}

/*
func IsValidOAuthInfo(oAuthBody OAuthBody) bool {
	log.Debug("IsValidOAuthInfo processing... ")

	conf := config.GetYamlConfig()
	for _, o := range conf.Auth {
		if oAuthBody.GrantType == o.Type && oAuthBody.UserName == o.User && oAuthBody.Value == o.Password {
			return true
		}
	}

	return false
}
*/

func IsWrongOAuthInfo(oAuthBody OAuthBody) bool {
	log.Debug("IsWrongOAuthInfo processing... ")

	if oAuthBody.GrantType == "" || strings.ToLower(oAuthBody.GrantType) != "password" ||
		oAuthBody.UserName == "" || oAuthBody.Value == "" {
		return true
	}

	return false
}

func GetTokenFromHttpRequest(r *http.Request) string {
	for k, v := range r.Header {
		log.Tracef("k:%s, v:%s", k, v)
		if strings.ToLower(k) == "accesstoken" && len(v) != 0 {
			log.Trace("AccessToken:", v[0])
			return v[0]
		}
	}

	return ""
}

// IsCarriedToken check token is carried
func IsCarriedToken(r *http.Request) (string, bool) {

	token := GetTokenFromHttpRequest(r)
	if token == "" {
		return "", false
	}
	return token, true
}

// Bcrypt Encrypt 加密明文密码
func BcryptEncrypt(password string) (string, error) {
	hashedBytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	return string(hashedBytes), err
}

// Bcrypt Compare 密文校验
func BcryptCompare(hashedPassword, password string) error {
	return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
}

// sha256 crypt
func GetSHA256HashCode(stringMessage string) string {
	message := []byte(stringMessage) //字符串转化字节数组
	//创建一个基于SHA256算法的hash.Hash接口的对象
	hash := sha256.New() //sha-256加密
	//输入数据
	hash.Write(message)
	//计算哈希值
	bytes := hash.Sum(nil)
	//将字符串编码为16进制格式,返回字符串
	hashCode := hex.EncodeToString(bytes)
	//返回哈希值
	return hashCode
}

// sha512 crypt
func GetSHA512HashCode(stringMessage string) string {
	message := []byte(stringMessage) //字符串转化字节数组
	//创建一个基于SHA256算法的hash.Hash接口的对象
	hash := sha512.New() //SHA-512加密
	//输入数据
	hash.Write(message)
	//计算哈希值
	bytes := hash.Sum(nil)
	//将字符串编码为16进制格式,返回字符串
	hashCode := hex.EncodeToString(bytes)
	//返回哈希值
	return hashCode
}
