// middleware/auth.go
package middleware

import (
	"context"
	"net/http"
	"strings"

	"api_bb/internal/models"
	"api_bb/internal/repository"
	"api_bb/internal/service"
	"api_bb/pkg/logger"

	"go.uber.org/zap"
)

type contextKey string

const (
	UserIDKey contextKey = "userID"
	UserKey   contextKey = "user"
)

func AuthMiddleware(jwtService service.JWTService, userRepo repository.UserRepository) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			var tokenString string
			logger := logger.Get()
			logger.Debug("authMiddleware Start")

			// Пробуем получить токен из заголовка Authorization
			authHeader := r.Header.Get("Authorization")
			if strings.HasPrefix(authHeader, "Bearer ") {
				tokenString = strings.TrimPrefix(authHeader, "Bearer ")
				logger.Debug("Token found in Authorization header")
			}

			// Если нет в заголовке, пробуем из куки
			if tokenString == "" {
				cookie, err := r.Cookie("auth_token")
				if err == nil {
					tokenString = cookie.Value
					logger.Debug("Token found in auth_token cookie")
				} else {
					logger.Debug("No auth_token cookie found", zap.Error(err))
				}
			}

			if tokenString == "" {
				logger.Debug("No token found in request")
				next.ServeHTTP(w, r)
				return
			}

			token, err := jwtService.ValidateToken(tokenString)
			if err != nil || !token.Valid {
				logger.Warn("Invalid token",
					zap.Error(err),
					zap.Bool("token_valid", token != nil && token.Valid))
				next.ServeHTTP(w, r)
				return
			}

			userID, err := jwtService.ExtractUserID(token)
			if err != nil {
				logger.Error("Failed to extract user ID from token",
					zap.Error(err))
				next.ServeHTTP(w, r)
				return
			}

			logger.Debug("Extracted user ID from token",
				zap.Any("user_id", userID))

			user, err := userRepo.FindByID(userID)
			if err != nil {
				logger.Error("Failed to find user by ID",
					zap.Any("user_id", userID),
					zap.Error(err))
				next.ServeHTTP(w, r)
				return
			}

			// Добавляем пользователя в контекст
			ctx := context.WithValue(r.Context(), UserIDKey, userID)
			ctx = context.WithValue(ctx, UserKey, user)

			logger.Debug("User authenticated successfully",
				zap.Any("user_id", userID),
				zap.String("username", user.FirstName))

				
			logger.Debug("authMiddleware End")
			next.ServeHTTP(w, r.WithContext(ctx))
		})
	}
}

// RequireAuth middleware требует аутентификации
func RequireAuth(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logger := logger.Get()
		userID := r.Context().Value(UserIDKey)
		logger.Debug("RequireAuth method start")
		logger.Debug("Extracted user ID from token",
			zap.Any("user_id", userID))

		if userID == nil {
			logger.Warn("Authentication required but no user ID in context")
			http.Error(w, `{"error": "Authentication required"}`, http.StatusUnauthorized)
			return
		}

		logger.Debug("User authenticated", zap.Any("user_id", userID))
			logger.Debug("authMiddleware End")
		next.ServeHTTP(w, r)
	})
}

// GetUserFromContext получает пользователя из контекста
func GetUserFromContext(ctx context.Context) (*models.User, bool) {
	logger := logger.Get()
	user, ok := ctx.Value(UserKey).(*models.User)
	logger.Debug("GetUserFromContext method")
	logger.Debug("Extracted user ID from token",
		zap.Any("user_id", user.ID))

	if !ok {
		logger.Debug("No user found in context")
	} else {
		logger.Debug("User retrieved from context",
			zap.Any("user_id", user.ID),
			zap.String("username", user.FirstName))
	}

	return user, ok
}
