package middleware

import (
	"DataShare/global"
	"DataShare/model"
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/twinj/uuid"
	"net/http"
	"time"
)

func CreateToken(appid string,appSecret string) (*model.TokenDetails, error) {
	td := &model.TokenDetails{}
	tokenExpire := global.SystemConfig.Redis.Cache.TokenExpire
	td.AtExpires = time.Now().Add(time.Second * time.Duration(tokenExpire)).Unix() //expires after 30 min
	td.TokenUuid = "uuid_"+uuid.NewV4().String()

	//td.RtExpires = time.Now().Add(time.Hour * 24 * 7).Unix()
	//td.RefreshUuid = td.TokenUuid + "++" + userId

	var err error
	//Creating Access Token
	atClaims := jwt.MapClaims{}
	atClaims["access_uuid"] = td.TokenUuid
	atClaims["appId"] = appid //账号
	atClaims["appSecret"] = appSecret //账号
	atClaims["exp"] = td.AtExpires
	at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims)
	td.AccessToken, err = at.SignedString([]byte(global.SystemConfig.Jwt.Signkey))
	if err != nil {
		return nil, err
	}

	//Creating Refresh Token
	refrushTokenExpire := global.SystemConfig.Redis.Cache.RefrushTokenExpire
	td.RtExpires = time.Now().Add(time.Second * time.Duration(refrushTokenExpire)).Unix()
	td.RefreshUuid = "uuid_"+td.TokenUuid + "++" + appid

	rtClaims := jwt.MapClaims{}
	rtClaims["refresh_uuid"] = td.RefreshUuid
	rtClaims["appId"] = appid //账号
	rtClaims["appSecret"] = appSecret //账号

	rtClaims["exp"] = td.RtExpires
	rt := jwt.NewWithClaims(jwt.SigningMethodHS256, rtClaims)

	td.RefreshToken, err = rt.SignedString([]byte(global.SystemConfig.Jwt.Signkey))
	if err != nil {
		return nil, err
	}
	return td, nil
}
func ExtractTokenMetadata(r *http.Request) (*model.AccessDetails, error) {
	token, err := VerifyToken(r)
	if err != nil {
		return nil, err
	}
	acc, err := Extract(token)
	if err != nil {
		return nil, err
	}
	return acc, nil
}

func TokenValid(r *http.Request) error {
	token, err := VerifyToken(r)
	if err != nil {
		return err
	}
	if _, ok := token.Claims.(jwt.Claims); !ok && !token.Valid {
		return err
	}
	return nil
}

func VerifyToken(r *http.Request) (*jwt.Token, error) {
	tokenString := ExtractToken(r)
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return []byte(global.SystemConfig.Jwt.Signkey), nil
	})
	if err != nil {
		return nil, err
	}
	return token, nil
}

//get the token from the request header
func ExtractToken(r *http.Request) string {
	bearToken := r.Header.Get("token")
	if len(bearToken) > 0 {
		return bearToken
	}
	return ""
}

func Extract(token *jwt.Token) (*model.AccessDetails, error) {

	claims, ok := token.Claims.(jwt.MapClaims)
	if ok && token.Valid {
		accessUuid, ok := claims["access_uuid"].(string)
		appId,appIdOk := claims["appId"].(string)
		appSecret,appSecretOk := claims["appSecret"].(string)
		if ok == false || appIdOk==false || appSecretOk==false {
			return nil, errors.New("unauthorized")
		} else {
			return &model.AccessDetails{
				TokenUuid: accessUuid,
				AppId: appId,
				AppSecret: appSecret,
			}, nil
		}
	}
	return nil, errors.New("something went wrong")
}
