package jwtx

import (
	"fmt"
	"time"

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

type Manager struct {
	issuer string
	secret []byte
	ttl    time.Duration
}

type AccessClaims struct {
	UserID       string `json:"uid"`
	UserPublicID string `json:"upub"`
	jwt.RegisteredClaims
}

func NewManager(issuer, secret string, ttl time.Duration) *Manager {
	return &Manager{
		issuer: issuer,
		secret: []byte(secret),
		ttl:    ttl,
	}
}

func (m *Manager) IssueAccessToken(userID, userPublicID string) (string, time.Time, error) {
	expiresAt := time.Now().UTC().Add(m.ttl)
	claims := AccessClaims{
		UserID:       userID,
		UserPublicID: userPublicID,
		RegisteredClaims: jwt.RegisteredClaims{
			Issuer:    m.issuer,
			Subject:   userID,
			ExpiresAt: jwt.NewNumericDate(expiresAt),
			IssuedAt:  jwt.NewNumericDate(time.Now().UTC()),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signed, err := token.SignedString(m.secret)
	if err != nil {
		return "", time.Time{}, err
	}
	return signed, expiresAt, nil
}

func (m *Manager) ParseAccessToken(tokenString string) (*AccessClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &AccessClaims{}, func(token *jwt.Token) (any, error) {
		if token.Method != jwt.SigningMethodHS256 {
			return nil, fmt.Errorf("unexpected signing method")
		}
		return m.secret, nil
	})
	if err != nil {
		return nil, err
	}

	claims, ok := token.Claims.(*AccessClaims)
	if !ok || !token.Valid {
		return nil, fmt.Errorf("invalid token claims")
	}
	return claims, nil
}
