package accounts

import (
	"fmt"
	"os"
	"time"

	"github.com/golang-jwt/jwt/v5"
)

type jwtClaims struct {
	UserID uint   `json:"user_id"`
	Role   string `json:"role"`
	jwt.RegisteredClaims
}

const (
	RoleAdmin = "admin"
	RoleUser  = "user"
)

func GenerateTokens(userID uint, role string) (string, string, error) {
	accessSecret := os.Getenv("JWT_SECRET")
	refreshSecret := os.Getenv("JWT_REFRESH_SECRET")
	if accessSecret == "" || refreshSecret == "" {
		return "", "", fmt.Errorf("JWT secrets are not set")
	}

	normalizedRole := normalizeRole(role)

	// Access Token
	accessClaims := jwtClaims{
		UserID: userID,
		Role:   normalizedRole,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
		},
	}
	accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims)
	accessString, err := accessToken.SignedString([]byte(accessSecret))
	if err != nil {
		return "", "", err
	}

	// Refresh Token
	refreshClaims := jwtClaims{
		UserID: userID,
		Role:   normalizedRole,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(7 * 24 * time.Hour)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
		},
	}
	refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims)
	refreshString, err := refreshToken.SignedString([]byte(refreshSecret))
	if err != nil {
		return "", "", err
	}

	return accessString, refreshString, nil
}

func ParseAccessToken(tokenString string) (uint, error) {
	secret := os.Getenv("JWT_SECRET")
	claims, err := parseTokenClaims(tokenString, secret)
	if err != nil {
		return 0, err
	}
	return claims.UserID, nil
}

func ParseRefreshToken(tokenString string) (uint, error) {
	secret := os.Getenv("JWT_REFRESH_SECRET")
	claims, err := parseTokenClaims(tokenString, secret)
	if err != nil {
		return 0, err
	}
	return claims.UserID, nil
}

func parseAccessClaims(tokenString string) (*jwtClaims, error) {
	secret := os.Getenv("JWT_SECRET")
	return parseTokenClaims(tokenString, secret)
}

func parseTokenClaims(tokenString, secret string) (*jwtClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &jwtClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(secret), nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*jwtClaims); ok && token.Valid {
		claims.Role = normalizeRole(claims.Role)
		return claims, nil
	}

	return nil, fmt.Errorf("invalid token")
}

func normalizeRole(role string) string {
	switch role {
	case RoleAdmin:
		return RoleAdmin
	default:
		return RoleUser
	}
}
