package auxjwt

import (
	"errors"
	"math"
	"time"

	"github.com/lestrrat-go/jwx/jwa"
	"github.com/lestrrat-go/jwx/jwt"
)

const (
	SessionIDKey = "sessionId"
	jwtAlg       = jwa.HS256
)

func MakeJWTToken(issuer, subject string, sessionID int64, lifetime time.Duration, secret []byte) ([]byte, int64, error) {
	var err error

	issueTime := time.Now()
	expireTime := time.Now().Add(lifetime)
	expireUnix := expireTime.Unix()

	token := jwt.New()
	token.Set(jwt.IssuerKey, issuer)
	token.Set(jwt.SubjectKey, subject)
	token.Set(jwt.ExpirationKey, expireTime)
	token.Set(jwt.IssuedAtKey, issueTime)
	token.Set(jwt.IssuedAtKey, issueTime)
	token.Set(SessionIDKey, sessionID)

	tokenBytes, err := jwt.Sign(token, jwtAlg, []byte(secret))
	if err != nil {
		return tokenBytes, expireUnix, err
	}
	return tokenBytes, expireUnix, err
}

func GetJWTSessionID(tokenBytes, secret []byte) (int64, error) {
	var err error
	var sessionID int64

	parseOpt := []jwt.ParseOption{
		jwt.WithVerify(jwtAlg, secret),
		jwt.WithValidator(jwt.IsExpirationValid()),
		jwt.WithValidate(true),
	}
	token, err := jwt.Parse(tokenBytes, parseOpt...)
	if err != nil {
		return sessionID, err
	}

	anyValue, exists := token.Get(SessionIDKey)
	if !exists {
		err := errors.New("Token does not include value is sessionId")
		return sessionID, err
	}

	fValue, convOk := anyValue.(float64)
	if !convOk {
		err := errors.New("Cannont convert value of sessionId to float64")
		return sessionID, err
	}

	sessionID = int64(math.Round(fValue))
	return sessionID, err
}

func ValidateJWTToken(tokenBytes []byte, issuer, subject string, secret []byte) error {

	parseOpt := []jwt.ParseOption{
		jwt.WithVerify(jwtAlg, secret),
		jwt.WithValidator(jwt.IsExpirationValid()),
		jwt.WithValidate(true),
	}
	token, err := jwt.Parse(tokenBytes, parseOpt...)
	if err != nil {
		return err
	}
	validateOpt := []jwt.ValidateOption{
		jwt.WithSubject(subject),
		jwt.WithIssuer(issuer),
		jwt.WithValidator(jwt.IsIssuedAtValid()),
		jwt.WithValidator(jwt.IsExpirationValid()),
	}
	err = jwt.Validate(token, validateOpt...)
	if err != nil {
		return err
	}

	return err
}
