package utils

import (
	"encoding/base64"
	"fmt"
	"time"

	"github.com/golang-jwt/jwt"
)

// !!!NOTE!!!: DO NOT USE IN SERIOUS SITUATIONS
// We use fixed keys to avoid problems with multi-instance backends.
// var hmacSampleSecret = cryptopasta.NewEncryptionKey()
const hmacKeyBase64 = `cb1XOnzisNX8IklvvyMquFdXx2ZWvqoIfNsnAor92XA=`

// Claims is a struct that will be encoded to a JWT.
type Claims struct {
	Data string `json:"data"`
	jwt.StandardClaims
}

func newClaims(issuer, data string, expireIn time.Duration) *Claims {
	return &Claims{
		Data: data,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(expireIn).Unix(),
			Issuer:    issuer,
		},
	}
}

// NewJWTString create a JWT string by given data, expire in 24 hours.
func NewJWTString(issuer, data string) (string, error) {
	return NewJWTStringWithExpire(issuer, data, 24*time.Hour)
}

func NewJWTStringWithExpire(issuer, data string, expireIn time.Duration) (string, error) {
	key, _ := base64.StdEncoding.DecodeString(hmacKeyBase64)
	claims := newClaims(issuer, data, expireIn)
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(key)
	if err != nil {
		return "", err
	}
	return tokenString, nil
}

// ParseJWTString parse the JWT string and return the raw data.
func ParseJWTString(requiredIssuer, tokenStr string) (string, error) {
	claims := &Claims{}
	token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (any, error) {
		key, _ := base64.StdEncoding.DecodeString(hmacKeyBase64)
		return key, nil
	})
	if err != nil {
		return "", err
	}
	if !token.Valid {
		return "", fmt.Errorf("token is invalid or expired")
	}
	if claims.Issuer != requiredIssuer {
		return "", fmt.Errorf("token is invalid (invalid issuer)")
	}
	return claims.Data, nil
}
