package jwt_utils

import (
	"time"

	"github.com/awnumar/memguard"
	jwt "github.com/golang-jwt/jwt/v5"
)

// GenerateJWTToken Generate the `access token` for the secret key.
// SECURITY: HMAC secret is wiped from memory after signing to prevent memory dump attacks.
func GenerateJWTToken(hmacSecret []byte, uuid string, ad time.Duration) (string, time.Time, error) {
	// SECURITY: Create a copy of the secret and wipe the copy after use
	// Note: The original hmacSecret is owned by the caller
	secretCopy := make([]byte, len(hmacSecret))
	copy(secretCopy, hmacSecret)
	defer memguard.WipeBytes(secretCopy) // SECURITY: Wipe secret copy after signing

	token := jwt.New(jwt.SigningMethodHS256)
	expiresIn := time.Now().Add(ad)

	// CWE-391: Safe type assertion even though we just created the token
	// Defensive programming to prevent future panics if jwt library changes
	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return "", expiresIn, jwt.ErrTokenInvalidClaims
	}

	claims["session_uuid"] = uuid
	claims["exp"] = expiresIn.Unix()

	tokenString, err := token.SignedString(secretCopy)
	if err != nil {
		return "", expiresIn, err
	}

	return tokenString, expiresIn, nil
}

// GenerateJWTTokenPair Generate the `access token` and `refresh token` for the secret key.
// SECURITY: HMAC secret is wiped from memory after signing to prevent memory dump attacks.
func GenerateJWTTokenPair(hmacSecret []byte, uuid string, ad time.Duration, rd time.Duration) (string, time.Time, string, time.Time, error) {
	// SECURITY: Create a copy of the secret and wipe the copy after use
	secretCopy := make([]byte, len(hmacSecret))
	copy(secretCopy, hmacSecret)
	defer memguard.WipeBytes(secretCopy) // SECURITY: Wipe secret copy after signing

	//
	// Generate token.
	//
	token := jwt.New(jwt.SigningMethodHS256)
	expiresIn := time.Now().Add(ad)

	// CWE-391: Safe type assertion even though we just created the token
	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return "", time.Now(), "", time.Now(), jwt.ErrTokenInvalidClaims
	}

	claims["session_uuid"] = uuid
	claims["exp"] = expiresIn.Unix()

	tokenString, err := token.SignedString(secretCopy)
	if err != nil {
		return "", time.Now(), "", time.Now(), err
	}

	//
	// Generate refresh token.
	//
	refreshToken := jwt.New(jwt.SigningMethodHS256)
	refreshExpiresIn := time.Now().Add(rd)

	// CWE-391: Safe type assertion for refresh token
	rtClaims, ok := refreshToken.Claims.(jwt.MapClaims)
	if !ok {
		return "", time.Now(), "", time.Now(), jwt.ErrTokenInvalidClaims
	}

	rtClaims["session_uuid"] = uuid
	rtClaims["exp"] = refreshExpiresIn.Unix()

	refreshTokenString, err := refreshToken.SignedString(secretCopy)
	if err != nil {
		return "", time.Now(), "", time.Now(), err
	}

	return tokenString, expiresIn, refreshTokenString, refreshExpiresIn, nil
}

// ProcessJWTToken validates either the `access token` or `refresh token` and returns either the `uuid` if success or error on failure.
// CWE-347: Implements proper algorithm validation to prevent JWT algorithm confusion attacks
// OWASP A02:2021: Cryptographic Failures - Prevents token forgery through algorithm switching
// SECURITY: HMAC secret copy is wiped from memory after validation.
func ProcessJWTToken(hmacSecret []byte, reqToken string) (string, error) {
	// SECURITY: Create a copy of the secret and wipe the copy after use
	secretCopy := make([]byte, len(hmacSecret))
	copy(secretCopy, hmacSecret)
	defer memguard.WipeBytes(secretCopy) // SECURITY: Wipe secret copy after validation

	token, err := jwt.Parse(reqToken, func(t *jwt.Token) (any, error) {
		// CRITICAL SECURITY FIX: Validate signing method to prevent algorithm confusion attacks
		// Protects against:
		// 1. "none" algorithm bypass (CVE-2015-9235)
		// 2. HS256/RS256 algorithm confusion (CVE-2016-5431)
		// 3. Token forgery through algorithm switching
		if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, jwt.ErrTokenSignatureInvalid
		}

		// Additional check: Ensure it's specifically HS256
		if t.Method.Alg() != "HS256" {
			return nil, jwt.ErrTokenSignatureInvalid
		}

		return secretCopy, nil
	})
	if err == nil && token.Valid {
		if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
			// Safe type assertion with validation
			sessionUUID, ok := claims["session_uuid"].(string)
			if !ok {
				return "", jwt.ErrTokenInvalidClaims
			}
			return sessionUUID, nil
		}
		return "", err
	}
	return "", err
}
