package auth

import (
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"git.schreifuchs.ch/schreifuchs/ng-blog/internal/model"
	jwt "github.com/golang-jwt/jwt/v5"
)

var ErrJWTInvalid = errors.New("JWT not valid")

func (s *Service) createJWT(user *model.User) (token string, err error) {
	claims := &Claims{
		Role:   user.Role,
		UserID: user.ID,
		RegisteredClaims: jwt.RegisteredClaims{
			Subject: user.UUID.String(),
			ExpiresAt: &jwt.NumericDate{
				Time: time.Now().Add(s.cfg.ValidDuration),
			},
		},
	}
	return jwt.NewWithClaims(jwt.SigningMethodHS512, claims).SignedString([]byte(s.cfg.Secret))
}

// validateJWT returns the token Claims and if token ist invalid ErrJWTInvalid
func (s *Service) validateJWT(tokenString string) (claims Claims, err error) {
	_, err = jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (any, error) {
		// Don't forget to validate the alg is what you expect:
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}

		return []byte(s.cfg.Secret), nil
	})
	if err != nil {
		return
	}

	if claims.ExpiresAt.Before(time.Now()) {
		err = ErrJWTInvalid
		return
	}

	// check if user has logged out this token
	var invalidated bool
	err = s.db.Model(&model.InvalidJWT{}).
		Select("count(*) > 0").
		Where("jwt = ?", tokenString).
		Find(&invalidated).
		Error

	if invalidated || err != nil {
		err = ErrJWTInvalid
		return
	}
	return
}

// extractToken extracts the Bearer token from the request
func extractToken(r *http.Request) (token string, err error) {
	tokenHeader := r.Header.Get("Authorization") // Grab the token from the header

	if tokenHeader == "" {
		err = errors.New("missing token")
		return
	}

	token = strings.TrimPrefix(tokenHeader, "Bearer ")

	if token == "" {
		err = errors.New("malformed token")
	}
	return
}
