package jwt

import (
	"fmt"
	"time"

	"git.javil.eu/jacob1123/budgeteer"
	"github.com/dgrijalva/jwt-go"
)

// TokenVerifier verifies Tokens
type TokenVerifier struct {
}

// Token contains everything to authenticate a user
type Token struct {
	username string
	name     string
	expiry   float64
	id       string
}

const (
	expiration = 72
	secret     = "uditapbzuditagscwxuqdflgzpbu´ßiaefnlmzeßtrubiadern"
)

// CreateToken creates a new token from username and name
func (tv *TokenVerifier) CreateToken(user *budgeteer.User) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"usr":  user.Email,
		"name": user.Name,
		"exp":  time.Now().Add(time.Hour * expiration).Unix(),
		"id":   user.ID,
	})

	// Generate encoded token and send it as response.
	t, err := token.SignedString([]byte(secret))
	if err != nil {
		return "", err
	}

	return t, nil
}

// VerifyToken verifys a given string-token
func (tv *TokenVerifier) VerifyToken(tokenString string) (budgeteer.Token, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}
		return []byte(secret), nil
	})
	if err != nil {
		return nil, err
	}

	claims, err := verifyToken(token)
	if err != nil {
		return nil, err
	}

	tkn := &Token{
		username: claims["usr"].(string),
		name:     claims["name"].(string),
		expiry:   claims["exp"].(float64),
		id:       claims["id"].(string),
	}
	return tkn, nil
}

func verifyToken(token *jwt.Token) (jwt.MapClaims, error) {
	if !token.Valid {
		return nil, fmt.Errorf("Token is not valid")
	}

	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return nil, fmt.Errorf("Claims are not of Type MapClaims")
	}

	if !claims.VerifyExpiresAt(time.Now().Unix(), true) {
		return nil, fmt.Errorf("Claims have expired")
	}

	return claims, nil
}

func (t *Token) GetName() string {
	return t.name
}

func (t *Token) GetUsername() string {
	return t.username
}

func (t *Token) GetExpiry() float64 {
	return t.expiry
}

func (t *Token) GetID() string {
	return t.id
}
