package middleware

import (
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	featureAuth "github.com/nexuscrm/backend/internal/features/auth"
	"github.com/nexuscrm/backend/pkg/apierr"
	"github.com/nexuscrm/shared/pkg/constants"
)

// RequireAuth is a middleware that validates JWT tokens
func RequireAuth(authSvc *featureAuth.AuthService) gin.HandlerFunc {
	return func(c *gin.Context) {
		// Get token from Authorization header
		authHeader := c.GetHeader(constants.HeaderAuthorization)
		if authHeader == "" {
			status, code, message := salesforceAuthError(c.Request.URL.Path, true)
			c.JSON(status, apierr.SFErrorResponse(code, message))
			c.Abort()
			return
		}

		// Extract token (format: "Bearer <token>")
		parts := strings.SplitN(authHeader, " ", 2)
		if len(parts) != 2 || parts[0] != "Bearer" {
			status, code, message := salesforceAuthError(c.Request.URL.Path, false)
			c.JSON(status, apierr.SFErrorResponse(code, message))
			c.Abort()
			return
		}

		tokenString := parts[1]

		// Validate token and session via AuthService
		claims, err := authSvc.ValidateSession(c.Request.Context(), tokenString)
		if err != nil {
			status, code, message := salesforceAuthError(c.Request.URL.Path, false)
			c.JSON(status, apierr.SFErrorResponse(code, message))
			c.Abort()
			return
		}

		// Update last activity (Fire and forget)
		authSvc.TouchSession(claims.RegisteredClaims.ID)

		// Set user session in context. NEVER stash the raw bearer token —
		// downstream capture middleware would otherwise persist it in
		// `_System_RequestCapture.SessionId`, making the capture table a
		// credential dump. Expose only the JWT's session identifier
		// (RegisteredClaims.ID / jti), which is non-secret and suffices
		// for audit correlation.
		c.Set(constants.ContextKeyUser, claims.User)
		c.Set(constants.ContextKeySessionID, claims.RegisteredClaims.ID)

		c.Next()
	}
}

func salesforceAuthError(path string, missingToken bool) (int, string, string) {
	if strings.HasPrefix(path, "/id/") && missingToken {
		return http.StatusForbidden, apierr.SFCodeMissingOAuthToken, "Missing OAuth token"
	}
	return http.StatusUnauthorized, apierr.SFCodeInvalidSessionID, "Session expired or invalid"
}

