package auth

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"

	"nexavpn/backend/internal/identity"
)

func NewRefreshToken() (plain string, hashed string, err error) {
	raw := make([]byte, 32)
	if _, err = rand.Read(raw); err != nil {
		return "", "", err
	}

	plain = base64.RawURLEncoding.EncodeToString(raw)
	sum := sha256.Sum256([]byte(plain))
	hashed = base64.RawURLEncoding.EncodeToString(sum[:])
	return plain, hashed, nil
}

func SignAccessToken(secret, issuer string, ttl time.Duration, claims identity.Claims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"iss":        issuer,
		"sub":        claims.UserID.String(),
		"username":   claims.Username,
		"role":       claims.Role,
		"session_id": claims.Session.String(),
		"exp":        time.Now().Add(ttl).Unix(),
		"iat":        time.Now().Unix(),
	})

	return token.SignedString([]byte(secret))
}

func ParseAccessToken(secret string, tokenString string) (identity.Claims, error) {
	claims := identity.Claims{}

	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
		return []byte(secret), nil
	})
	if err != nil || !token.Valid {
		return claims, err
	}

	mapClaims, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return claims, jwt.ErrTokenMalformed
	}

	subject, ok := mapClaims["sub"].(string)
	if !ok {
		return claims, jwt.ErrTokenMalformed
	}
	sessionValue, ok := mapClaims["session_id"].(string)
	if !ok {
		return claims, jwt.ErrTokenMalformed
	}

	userID, err := uuid.Parse(subject)
	if err != nil {
		return claims, err
	}
	sessionID, err := uuid.Parse(sessionValue)
	if err != nil {
		return claims, err
	}

	claims.UserID = userID
	claims.Session = sessionID
	claims.Username, _ = mapClaims["username"].(string)
	claims.Role, _ = mapClaims["role"].(string)
	return claims, nil
}
