package middleware

import (
	"eslogad-be/internal/appcontext"
	"net/http"
	"strings"

	"eslogad-be/internal/constants"
	"eslogad-be/internal/contract"
	"eslogad-be/internal/logger"

	"github.com/gin-gonic/gin"
)

type AuthMiddleware struct {
	authService AuthValidateService
}

func NewAuthMiddleware(authService AuthValidateService) *AuthMiddleware {
	return &AuthMiddleware{
		authService: authService,
	}
}

func (m *AuthMiddleware) RequireAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		token := m.extractTokenFromHeader(c)
		if token == "" {
			logger.FromContext(c.Request.Context()).Error("AuthMiddleware::RequireAuth -> Missing authorization token")
			m.sendErrorResponse(c, "Authorization token is required", http.StatusUnauthorized)
			c.Abort()
			return
		}

		userResponse, err := m.authService.ValidateToken(token)
		if err != nil {
			logger.FromContext(c.Request.Context()).WithError(err).Error("AuthMiddleware::RequireAuth -> Invalid token")
			m.sendErrorResponse(c, "Invalid or expired token", http.StatusUnauthorized)
			c.Abort()
			return
		}

		setKeyInContext(c, appcontext.UserIDKey, userResponse.ID.String())
		if len(userResponse.DepartmentResponse) > 0 {
			departmentID := userResponse.DepartmentResponse[0].ID.String()
			setKeyInContext(c, appcontext.DepartmentIDKey, departmentID)
		} else {
			setKeyInContext(c, appcontext.DepartmentIDKey, "")
		}

		if roles, perms, err := m.authService.ExtractAccess(token); err == nil {
			c.Set("user_roles", roles)
			c.Set("user_permissions", perms)
		}

		logger.FromContext(c.Request.Context()).Infof("AuthMiddleware::RequireAuth -> User authenticated: %s", userResponse.Email)
		c.Next()
	}
}

func (m *AuthMiddleware) RequireRole(allowedRoles ...string) gin.HandlerFunc {
	return func(c *gin.Context) {
		appCtx := appcontext.FromGinContext(c.Request.Context())

		hasRequiredRole := false
		for _, role := range allowedRoles {
			if appCtx.UserRole == role {
				hasRequiredRole = true
				break
			}
		}

		if !hasRequiredRole {
			m.sendErrorResponse(c, "Insufficient permissions", http.StatusForbidden)
			c.Abort()
			return
		}

		c.Next()
	}
}

func (m *AuthMiddleware) RequireAdminOrManager() gin.HandlerFunc {
	return m.RequireRole("admin", "manager")
}

func (m *AuthMiddleware) RequireAdmin() gin.HandlerFunc {
	return m.RequireRole("admin")
}

func (m *AuthMiddleware) RequireSuperAdmin() gin.HandlerFunc {
	return m.RequireRole("superadmin")
}

func (m *AuthMiddleware) RequireActiveUser() gin.HandlerFunc {
	return func(c *gin.Context) {
		userResponse, exists := c.Get("user")
		if !exists {
			logger.FromContext(c.Request.Context()).Error("AuthMiddleware::RequireActiveUser -> User not authenticated")
			m.sendErrorResponse(c, "Authentication required", http.StatusUnauthorized)
			c.Abort()
			return
		}

		user, ok := userResponse.(*contract.UserResponse)
		if !ok {
			logger.FromContext(c.Request.Context()).Error("AuthMiddleware::RequireActiveUser -> Invalid user context")
			m.sendErrorResponse(c, "Invalid user context", http.StatusInternalServerError)
			c.Abort()
			return
		}

		if !user.IsActive {
			logger.FromContext(c.Request.Context()).Errorf("AuthMiddleware::RequireActiveUser -> User account is deactivated: %s", user.Email)
			m.sendErrorResponse(c, "User account is deactivated", http.StatusForbidden)
			c.Abort()
			return
		}

		logger.FromContext(c.Request.Context()).Infof("AuthMiddleware::RequireActiveUser -> Active user check passed: %s", user.Email)
		c.Next()
	}
}

func (m *AuthMiddleware) RequirePermissions(required ...string) gin.HandlerFunc {
	return func(c *gin.Context) {
		if _, exists := c.Get("user_permissions"); !exists {
			m.sendErrorResponse(c, "Authentication required", http.StatusUnauthorized)
			c.Abort()
			return
		}

		permIface, _ := c.Get("user_permissions")
		perms, _ := permIface.([]string)
		userPerms := map[string]bool{}
		for _, code := range perms {
			userPerms[code] = true
		}

		for _, need := range required {
			if !userPerms[need] {
				m.sendErrorResponse(c, "Insufficient permissions", http.StatusForbidden)
				c.Abort()
				return
			}
		}

		c.Next()
	}
}

func (m *AuthMiddleware) extractTokenFromHeader(c *gin.Context) string {
	authHeader := c.GetHeader("Authorization")
	if authHeader == "" {
		return ""
	}

	parts := strings.Split(authHeader, " ")
	if len(parts) != 2 || parts[0] != "Bearer" {
		return ""
	}

	return parts[1]
}

func (m *AuthMiddleware) sendErrorResponse(c *gin.Context, message string, statusCode int) {
	errorResponse := &contract.ErrorResponse{
		Error:   "auth_error",
		Message: message,
		Code:    statusCode,
		Details: map[string]interface{}{
			"entity": constants.AuthHandlerEntity,
		},
	}
	c.JSON(statusCode, errorResponse)
}
