package token

import (
	"encoding/hex"
	"errors"
	"time"

	"aidanwoods.dev/go-paseto"
)

const TokenDuration = 7 * 24 * time.Hour

const (
	claimUserID = "uid"
)

// KeyFromHex loads a V4 symmetric key from a 64-char hex string (AUTH_PASETO_KEY).
func KeyFromHex(h string) (paseto.V4SymmetricKey, error) {
	b, err := hex.DecodeString(h)
	if err != nil {
		return paseto.V4SymmetricKey{}, err
	}
	return paseto.V4SymmetricKeyFromBytes(b)
}

// IssueToken creates a PASETO v4 local (encrypted) token carrying the
// user ID and a jti that maps to a row in the sessions table.
func IssueToken(key paseto.V4SymmetricKey, userID, jti string) (string, error) {
	tok := paseto.NewToken()
	tok.SetIssuedAt(time.Now())
	tok.SetNotBefore(time.Now())
	tok.SetExpiration(time.Now().Add(TokenDuration))
	tok.SetJti(jti)
	if err := tok.Set(claimUserID, userID); err != nil {
		return "", err
	}
	return tok.V4Encrypt(key, nil), nil
}

// Claims holds the verified payload extracted from a token.
type Claims struct {
	UserID string
	JTI    string
}

// VerifyToken decrypts and validates a PASETO v4 local token.
func VerifyToken(key paseto.V4SymmetricKey, raw string) (Claims, error) {
	parser := paseto.NewParser()
	parser.AddRule(paseto.NotExpired())
	parser.AddRule(paseto.ValidAt(time.Now()))

	tok, err := parser.ParseV4Local(key, raw, nil)
	if err != nil {
		return Claims{}, err
	}

	var userID string
	if err := tok.Get(claimUserID, &userID); err != nil {
		return Claims{}, errors.New("missing uid claim")
	}
	jti, err := tok.GetJti()
	if err != nil {
		return Claims{}, errors.New("missing jti claim")
	}

	return Claims{UserID: userID, JTI: jti}, nil
}
