package main

import (
	b64 "encoding/base64"
	"io/ioutil"
	"time"

	jwt "github.com/appleboy/gin-jwt/v2"
	"github.com/gin-gonic/gin"
	gojwt "github.com/golang-jwt/jwt/v4"
	log "github.com/sirupsen/logrus"
)

// func makeTokenString(SigningAlgorithm string, username string, cfg *Config) string {
// 	if SigningAlgorithm == "" {
// 		SigningAlgorithm = "HS256"
// 	}
// 	token := jwt.New(jwt.GetSigningMethod(SigningAlgorithm))
// 	claims := token.Claims.(jwt.MapClaims)
// 	claims["identity"] = username
// 	claims["exp"] = time.Now().Add(time.Hour).Unix()
// 	claims["orig_iat"] = time.Now().Unix()
// 	var tokenString string
// 	if SigningAlgorithm == "RS256" {
// 		keyData, _ := ioutil.ReadFile(cfg.KeyPem)
// 		signKey, _ := jwt.ParseRSAPrivateKeyFromPEM(keyData)
// 		tokenString, _ = token.SignedString(signKey)
// 	} else {
// 		tokenString, _ = token.SignedString(key)
// 	}
// 	return tokenString
// }
// func makeTokenString(mw *jwt.GinJWTMiddleware,data interface{}) (string,error) {
func makeTokenString(rtenv *RouteEnv,data interface{}) (string,error) {
	if rtenv.AuthMiddleware == nil {
	  return "", nil
	}
	token := gojwt.New(gojwt.GetSigningMethod(rtenv.AuthMiddleware.SigningAlgorithm))
	claims := token.Claims.(gojwt.MapClaims)
	if rtenv.AuthMiddleware.PayloadFunc != nil {
		for key, value := range rtenv.AuthMiddleware.PayloadFunc(data) {
			claims[key] = value
		}
	}
	expire := rtenv.AuthMiddleware.TimeFunc().Add(rtenv.AuthMiddleware.Timeout)
	claims["exp"] = expire.Unix()
	claims["orig_iat"] = rtenv.AuthMiddleware.TimeFunc().Unix()
	var tokenString string
	var err error
 	if rtenv.Cfg.SigningAlgorithm == "RS256" {
	// if mw.usingPublicKeyAlgo() {
		keyData, _ := ioutil.ReadFile(rtenv.Cfg.JwtKeyPem)
  	signKey, _ := gojwt.ParseRSAPrivateKeyFromPEM(keyData)
 		tokenString, err = token.SignedString(signKey)
	} else {
	  tokenString, err = token.SignedString(rtenv.AuthMiddleware.Key)
	}
	return tokenString, err
}
func getJwt(rtenv *RouteEnv)*jwt.GinJWTMiddleware {
	// the jwt middleware
	authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
		Realm:       rtenv.Cfg.JwtRealm,
		Key:         []byte(rtenv.Cfg.JwtKey),
		Timeout:     time.Duration(rtenv.Cfg.JwtTimeout) * time.Minute,
		MaxRefresh:  time.Duration(rtenv.Cfg.JwtMaxRefresh) * time.Minute,
		IdentityKey: rtenv.Cfg.IdentityKey,
		PrivKeyFile: rtenv.Cfg.KeyPem,
		PubKeyFile:  rtenv.Cfg.CertPem,
		PayloadFunc: func(data interface{}) jwt.MapClaims {
			if v, ok := data.(*User); ok {
				return jwt.MapClaims{
					rtenv.Cfg.IdentityKey: v.UserName,
					"uuid": v.UUID,
					"data": v.Data,
				}
			}
			return jwt.MapClaims{}
		},
		IdentityHandler: func(c *gin.Context) interface{} {
			claims := jwt.ExtractClaims(c)
			username := ""
			name, okname := claims[rtenv.Cfg.IdentityKey].(string) 
			if (okname) {
				username = name
			}
			return &User{ UserName: username }
		},
		Authenticator: func(c *gin.Context) (interface{}, error) {
			var loginVals Login
			if err := c.ShouldBind(&loginVals); err != nil {
				return "", jwt.ErrMissingLoginValues
			}
			userID := loginVals.Username
			password := loginVals.Password
			if val, ok := rtenv.Users.Accounts[userID]; ok {
				pasw,_ := b64.StdEncoding.DecodeString(password)
	     	pass := string(pasw)
			  txtdata,err := decrypt(val.Passwd, string(CRYPTKEY))
			  if err == nil && txtdata == pass {
					return &User{
						UserName:  val.Id,
						LastName:  "",
						FirstName: "",
					}, nil
				}
			}
			return nil, jwt.ErrFailedAuthentication
		},
		Authorizator: func(data interface{}, c *gin.Context) bool {
			if v, ok := data.(*User); ok {
				if v.UserName == rtenv.Cfg.PubUser {
					 return true
				}
				if _, ok := rtenv.Users.Accounts[v.UserName]; ok {
				  return true
			  } else {
			    return false
				}
			}
			return false
		},
		Unauthorized: func(c *gin.Context, code int, message string) {
			c.JSON(code, gin.H{
				"code":    code,
				"message": message,
			})
		},
		// TokenLookup is a string in the form of "<source>:<name>" that is used
		// to extract token from the request.
		// Optional. Default value "header:Authorization".
		// Possible values:
		// - "header:<name>"
		// - "query:<name>"
		// - "cookie:<name>"
		// - "param:<name>"
		TokenLookup: "header: Authorization, query: token, cookie: jwt",
		// TokenLookup: "query:token",
		// TokenLookup: "cookie:token",

		// TokenHeadName is a string in the header. Default value is "Bearer"
		TokenHeadName: "Bearer",
		// TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens.
		TimeFunc: time.Now,
	})
	if err != nil {
		log.Fatal("JWT Error:" + err.Error())
	}
	return authMiddleware
}

// func refreshToken(c *gin.Context, rtenv *RouteEnv,data interface{}) (string, time.Time, error) {
// 	var expire time.Time
// 	claims, err := rtenv.AuthMiddleware.CheckIfTokenExpire(c)
// 	if err != nil {
// 		return "", expire, err
// 	}
// 	fmt.Printf("Refresh token: %v\n",claims)
// 	var token string
// 	token,err = makeTokenString(rtenv.AuthMiddleware,data)
// 	expire = time.Now()

// 	// // set cookie
// 	// if rtenv.AuthMiddleware.SendCookie {
// 	// 	expireCookie := rtenv.AuthMiddleware.TimeFunc().Add(rtenv.AuthMiddleware.CookieMaxAge)
// 	// 	maxage := int(expireCookie.Unix() - time.Now().Unix())

// 	// 	if rtenv.AuthMiddleware.CookieSameSite != 0 {
// 	// 		c.SetSameSite(rtenv.AuthMiddleware.CookieSameSite)
// 	// 	}

// 	// 	c.SetCookie(
// 	// 		rtenv.AuthMiddleware.CookieName,
// 	// 		tokenString,
// 	// 		maxage,
// 	// 		"/",
// 	// 		rtenv.AuthMiddleware.CookieDomain,
// 	// 		rtenv.AuthMiddleware.SecureCookie,
// 	// 		rtenv.AuthMiddleware.CookieHTTPOnly,
// 	// 	)
// 	// }
// 	return token, expire, nil
// }