package utils

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"

	"abuse_registration_poc/internal/config"
)

func getAPIKey() (string, error) {
	apiKey := config.ApiKey
	if apiKey == "" {
		return "", fmt.Errorf("API key is not set in the configuration")
	}
	return apiKey, nil
}

func GenerateToken(userName string, userID int64, role string) (string, error) {
	apiKey, err := getAPIKey()
	if err != nil {
		return "", err
	}

	header := map[string]string{"alg": "HS256", "typ": "JWT"}
	claims := map[string]any{
		"userName": userName,
		"userId":   userID,
		"role":     role,
		"exp":      time.Now().UTC().Add(2 * time.Hour).Unix(),
	}

	headerBytes, err := json.Marshal(header)
	if err != nil {
		return "", err
	}
	claimsBytes, err := json.Marshal(claims)
	if err != nil {
		return "", err
	}

	unsigned := base64.RawURLEncoding.EncodeToString(headerBytes) + "." + base64.RawURLEncoding.EncodeToString(claimsBytes)
	return unsigned + "." + sign(unsigned, apiKey), nil
}

func VerifyToken(token string) (int64, string, error) {
	apiKey, err := getAPIKey()
	if err != nil {
		return 0, "", err
	}

	parts := strings.Split(token, ".")
	if len(parts) != 3 {
		return 0, "", errors.New("invalid token")
	}

	unsigned := parts[0] + "." + parts[1]
	expected := sign(unsigned, apiKey)
	if !hmac.Equal([]byte(expected), []byte(parts[2])) {
		return 0, "", errors.New("invalid signature")
	}

	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return 0, "", err
	}

	var claims map[string]any
	if err := json.Unmarshal(payload, &claims); err != nil {
		return 0, "", err
	}

	role, ok := claims["role"].(string)
	if !ok {
		return 0, "", errors.New("invalid role claim")
	}
	userIDFloat, ok := claims["userId"].(float64)
	if !ok {
		return 0, "", errors.New("invalid userId claim")
	}
	exp, ok := claims["exp"].(float64)
	if !ok || time.Now().UTC().Unix() > int64(exp) {
		return 0, "", errors.New("token expired")
	}

	return int64(userIDFloat), role, nil
}

func sign(unsigned string, apiKey string) string {
	mac := hmac.New(sha256.New, []byte(apiKey))
	mac.Write([]byte(unsigned))
	return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
}
