package middleware

import (
	"strings"
	"system-altrak/internal/config"
	"system-altrak/pkg/utils"

	"github.com/gofiber/fiber/v2"
)

func extractTokenFromRequest(c *fiber.Ctx) (string, bool) {
	authHeader := strings.TrimSpace(c.Get("Authorization"))
	if authHeader != "" {
		if !strings.HasPrefix(strings.ToLower(authHeader), "bearer ") {
			return "", false
		}
		return strings.TrimSpace(authHeader[len("Bearer "):]), true
	}

	cookieToken := strings.TrimSpace(c.Cookies("access_token"))
	if cookieToken != "" {
		return cookieToken, true
	}

	queryToken := strings.TrimSpace(c.Query("token"))
	if queryToken != "" {
		return "", false
	}

	return "", true
}

func JWTMiddleware(cfg *config.Config) fiber.Handler {
	return func(c *fiber.Ctx) error {
		tokenString, validScheme := extractTokenFromRequest(c)
		if !validScheme {
			return utils.UnauthorizedResponse(c, "Skema token tidak valid")
		}

		if tokenString == "" {
			return utils.UnauthorizedResponse(c, "Autentikasi diperlukan")
		}

		claims, err := utils.ValidateToken(tokenString, cfg.JWTSecret)
		if err != nil {
			return utils.UnauthorizedResponse(c, "Token tidak valid atau kadaluarsa")
		}

		c.Locals("access_token", tokenString)
		c.Locals("user_id", claims.UserID)
		c.Locals("username", claims.Username)
		c.Locals("role", claims.Role)
		c.Locals("branch_id", claims.BranchID)
		return c.Next()
	}
}

// OptionalJWTMiddleware enriches context with user claims when a valid token exists.
func OptionalJWTMiddleware(cfg *config.Config) fiber.Handler {
	return func(c *fiber.Ctx) error {
		tokenString, validScheme := extractTokenFromRequest(c)
		if !validScheme {
			return utils.UnauthorizedResponse(c, "Skema token tidak valid")
		}

		if tokenString == "" {
			return c.Next()
		}

		claims, err := utils.ValidateToken(tokenString, cfg.JWTSecret)
		if err != nil {
			return utils.UnauthorizedResponse(c, "Token tidak valid atau kadaluarsa")
		}

		c.Locals("access_token", tokenString)
		c.Locals("user_id", claims.UserID)
		c.Locals("username", claims.Username)
		c.Locals("role", claims.Role)
		c.Locals("branch_id", claims.BranchID)
		return c.Next()
	}
}

// OptionalJWTMiddlewareAnonymous allows public endpoints like /csrf-token to
// continue even when a stale or invalid auth token is present.
func OptionalJWTMiddlewareAnonymous(cfg *config.Config) fiber.Handler {
	return func(c *fiber.Ctx) error {
		tokenString, validScheme := extractTokenFromRequest(c)
		if !validScheme || tokenString == "" {
			return c.Next()
		}

		claims, err := utils.ValidateToken(tokenString, cfg.JWTSecret)
		if err != nil {
			return c.Next()
		}

		c.Locals("access_token", tokenString)
		c.Locals("user_id", claims.UserID)
		c.Locals("username", claims.Username)
		c.Locals("role", claims.Role)
		c.Locals("branch_id", claims.BranchID)
		return c.Next()
	}
}
