// internal/service/jwt_service.go
package service

import (
	"errors"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"go.mongodb.org/mongo-driver/bson/primitive"
)

type JWTService interface {
	GenerateAccessToken(userID primitive.ObjectID) (string, error)
	GenerateRefreshToken() (string, error)
	ValidateAccessToken(tokenString string) (*jwt.Token, error)
	ExtractUserID(token *jwt.Token) (primitive.ObjectID, error)
}

type jwtService struct {
	secretKey       string
	accessTokenTTL  time.Duration
	refreshTokenTTL time.Duration
}

type Claims struct {
	UserID primitive.ObjectID `json:"user_id"`
	jwt.RegisteredClaims
}

func NewJWTService(secretKey string) JWTService {
	return &jwtService{
		secretKey:       secretKey,
		accessTokenTTL:  7 * 24 * time.Hour,  // 7 days
		refreshTokenTTL: 14 * 24 * time.Hour, // 14 days

	}
}

func (s *jwtService) GenerateAccessToken(userID primitive.ObjectID) (string, error) {
	claims := Claims{
		UserID: userID,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(s.accessTokenTTL)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Subject:   userID.Hex(),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(s.secretKey))
}

func (s *jwtService) GenerateRefreshToken() (string, error) {
	claims := jwt.RegisteredClaims{
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(s.refreshTokenTTL)),
		IssuedAt:  jwt.NewNumericDate(time.Now()),
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(s.secretKey))
}

func (s *jwtService) ValidateAccessToken(tokenString string) (*jwt.Token, error) {
	return jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, errors.New("invalid signing method")
		}
		return []byte(s.secretKey), nil
	})
}

func (s *jwtService) ExtractUserID(token *jwt.Token) (primitive.ObjectID, error) {
	claims, ok := token.Claims.(*Claims)
	if !ok {
		return primitive.NilObjectID, errors.New("invalid token claims")
	}
	return claims.UserID, nil
}
