package token

import (
	"time"

	"aidanwoods.dev/go-paseto"
	"github.com/google/uuid"
)

// PasetoMaker is a PASETO token maker
type PasetoMaker struct {
	privateKey paseto.V4AsymmetricSecretKey
	publicKey  paseto.V4AsymmetricPublicKey
	parser     paseto.Parser
}

// NewPasetoMaker creates a new PasetoMaker
func NewPasetoMaker(privateKeyHex string) (Maker, error) {
	privateKey, err := paseto.NewV4AsymmetricSecretKeyFromHex(privateKeyHex)
	if err != nil {
		return nil, err
	}

	maker := &PasetoMaker{
		privateKey: privateKey,
		publicKey:  privateKey.Public(),
		parser:     paseto.NewParser(),
	}

	return maker, nil
}

// CreateToken creates a new token for a specific username and duration
func (maker *PasetoMaker) CreateToken(email string, duration time.Duration) (string, *Payload, error) {
	payload, err := NewPayload(email, duration)
	if err != nil {
		return "", payload, err
	}

	token := paseto.NewToken()
	token.SetNotBefore(time.Now())
	token.SetIssuedAt(payload.IssuedAt)
	token.SetExpiration(payload.ExpiredAt)
	token.SetString("id", payload.ID.String())
	token.SetString("email", payload.Email)

	signed := token.V4Sign(maker.privateKey, nil)
	return signed, payload, err
}

// VerifyToken checks if the token is valid or not
func (maker *PasetoMaker) VerifyToken(token string) (*Payload, error) {
	payload := &Payload{}

	t, err := maker.parser.ParseV4Public(maker.publicKey, token, nil)
	if err != nil {
		return nil, err
	}

	payload.ExpiredAt, err = t.GetExpiration()
	if err != nil {
		return nil, ErrInvalidToken
	}

	payload.IssuedAt, err = t.GetIssuedAt()
	if err != nil {
		return nil, ErrInvalidToken
	}

	payload.Email, err = t.GetString("email")
	if err != nil {
		return nil, ErrInvalidToken
	}

	uid, err := t.GetString("id")
	if err != nil {
		return nil, ErrInvalidToken
	}

	payload.ID = uuid.MustParse(uid)
	err = payload.Valid()
	if err != nil {
		return nil, err
	}

	return payload, nil
}