package middlewares

import (
	"GoMembership/internal/config"
	"GoMembership/pkg/logger"
	"fmt"
	"net/http"
	"time"

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

var (
	jwtKey           = []byte(config.Auth.JWTSecret)
	jwtSigningMethod = jwt.SigningMethodHS256
	jwtParser        = jwt.NewParser(jwt.WithValidMethods([]string{jwtSigningMethod.Alg()}))
)

func GenerateToken(userID int64) (string, error) {
	token := jwt.NewWithClaims(jwtSigningMethod, jwt.MapClaims{
		"user_id": userID,
		"exp":     time.Now().Add(time.Minute * 10).Unix(), // Token expires in 10 Minutes
	})

	logger.Error.Printf("token generated: %#v", token)
	return token.SignedString(jwtKey)
}

func verifyToken(tokenString string) (*jwt.Token, error) {
	if tokenString == "" {
		return nil, fmt.Errorf("Authorization token is required")
	}
	token, err := jwtParser.Parse(tokenString, func(_ *jwt.Token) (interface{}, error) {
		return jwtKey, nil
	})

	if err != nil {
		return nil, err
	}

	if !token.Valid {
		return nil, fmt.Errorf("invalid token")
	}

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

	exp, ok := claims["exp"].(float64)
	if !ok {
		return nil, fmt.Errorf("invalid expiration claim")
	}

	userID, ok := claims["user_id"].(float64)
	if !ok {
		logger.Error.Printf("Invalid user ID: %v", userID)
		return nil, fmt.Errorf("Invalid user ID")
	}

	if time.Now().Unix() > int64(exp) {
		return nil, fmt.Errorf("token expired")
	}

	return token, nil
}

func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		tokenString, err := c.Cookie("jwt")
		if err != nil {
			logger.Error.Printf("No Auth token: %v\n", err)
			c.JSON(http.StatusUnauthorized, gin.H{"error": "No Auth token"})
			c.Abort()
			return
		}

		token, err := verifyToken(tokenString)
		if err != nil {
			logger.Error.Printf("Token is invalid: %v\n", err)
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Auth token invalid"})
			c.Abort()
			return
		}
		claims, _ := token.Claims.(jwt.MapClaims)
		userID, _ := claims["user_id"].(float64)

		// Generate a new token
		newToken, err := GenerateToken(int64(userID))
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to refresh token"})
			c.Abort()
			return
		}

		c.SetCookie(
			"jwt",
			newToken,
			10*60, // 10 minutes
			"/",
			"",
			true,
			true,
		)
		c.Set("user_id", userID)
		c.Next()
	}
}
