package helper

import (
	"context"
	"fmt"
	"github.com/golang-jwt/jwt"
	"gitlab.zalopay.vn/top/intern/vybnt/gallery-backend/gallery/repository"
	"os"
	"strconv"
	"time"
)

//JWTService is a contract of what jwtService can do
type JWTService interface {
	GenerateToken(userID string) string
	ValidateToken(token string) (*jwt.Token, error)
	ExtractUser(token string, userRepo repository.UserRepository) interface{}
}

type jwtCustomClaim struct {
	UserID string `json:"user_id"`
	jwt.StandardClaims
}

type jwtService struct {
	secretKey string
	issuer    string
}

//NewJWTService method is creates a new instance of JWTService
func NewJWTService() JWTService {
	return &jwtService{
		issuer:    "ashpex",
		secretKey: getSecretKey(),
	}
}

func getSecretKey() string {
	secretKey := os.Getenv("JWT_SECRET")
	if secretKey != "" {
		secretKey = "ashpex"
	}
	return secretKey
}

func (j *jwtService) GenerateToken(UserID string) string {
	claims := &jwtCustomClaim{
		UserID,
		jwt.StandardClaims{
			ExpiresAt: time.Now().AddDate(1, 0, 0).Unix(),
			Issuer:    j.issuer,
			IssuedAt:  time.Now().Unix(),
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	t, err := token.SignedString([]byte(j.secretKey))
	if err != nil {
		fmt.Sprintf("Error: %v", err)
	}
	return t
}

func (j *jwtService) ValidateToken(token string) (*jwt.Token, error) {
	return jwt.Parse(token, func(t_ *jwt.Token) (interface{}, error) {
		if _, ok := t_.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("Unexpected signing method %v", t_.Header["alg"])
		}
		return []byte(j.secretKey), nil
	})
}

func (j *jwtService) getUserIDByToken(token string, ctx context.Context) string {
	aToken, err := j.ValidateToken(token)
	if err != nil {
		panic(err.Error())
	}
	claims := aToken.Claims.(jwt.MapClaims)
	id := fmt.Sprintf("%v", claims["user_id"])
	return id
}

func (j *jwtService) ExtractUser(token string, userRepo repository.UserRepository) interface{} {
	t, _ := j.ValidateToken(token)

	if t == nil {
		return nil
	}

	fUserID := t.Claims.(jwt.MapClaims)["user_id"].(float64)
	userID := strconv.FormatFloat(fUserID, 'E', -1, 64)
	user, _ := userRepo.FindByUserID(userID)
	return user
}
