// handlers/user_stats_handler.go
package handlers

import (
	"net/http"
	"strconv"

	"api_bb/internal/service"
	"api_bb/pkg/logger"
	"api_bb/pkg/middleware"
	"api_bb/pkg/utils"

	"github.com/go-chi/chi/v5"
	"go.uber.org/zap"
)

type UserStatsHandler struct {
	logger         logger.LoggerInterface
	userStatsService service.UserStatsService
}

func NewUserStatsHandler(userStatsService service.UserStatsService) *UserStatsHandler {
	return &UserStatsHandler{
		logger:         logger.NewWrapper(logger.Get().With(zap.String("handler", "user_stats"))),
		userStatsService: userStatsService,
	}
}

// GetUserStats возвращает статистику текущего пользователя
func (h *UserStatsHandler) GetUserStats(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get user stats request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Получаем пользователя из контекста
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("get user stats failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Получаем статистику через сервис
	stats, err := h.userStatsService.GetUserStats(user.ID)
	if err != nil {
		h.logger.Error("failed to get user stats from service",
			zap.Uint("user_id", user.ID),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get user stats: "+err.Error())
		return
	}

	h.logger.Info("user stats retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.Float64("total_distance", stats.TotalDistance),
		zap.Int("workouts_count", stats.WorkoutsCount),
	)

	utils.RespondWithJSON(w, http.StatusOK, stats)
}

// GetUserStatsByID возвращает статистику пользователя по ID (для администраторов)
func (h *UserStatsHandler) GetUserStatsByID(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get user stats by ID request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Получаем текущего пользователя для проверки прав
	currentUser, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("get user stats by ID failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Проверяем права администратора
	if currentUser.Role != "admin" {
		h.logger.Warn("get user stats by ID failed - insufficient permissions",
			zap.Uint("user_id", currentUser.ID),
			zap.String("role", currentUser.Role),
		)
		utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
		return
	}

	// Получаем ID пользователя из параметров URL
	userIDStr := chi.URLParam(r, "userID")
	userID, err := strconv.ParseUint(userIDStr, 10, 32)
	if err != nil {
		h.logger.Warn("invalid user ID parameter",
			zap.String("user_id_param", userIDStr),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid user ID")
		return
	}

	// Получаем статистику через сервис
	stats, err := h.userStatsService.GetUserStats(uint(userID))
	if err != nil {
		h.logger.Error("failed to get user stats by ID from service",
			zap.Uint("target_user_id", uint(userID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get user stats: "+err.Error())
		return
	}

	h.logger.Info("user stats by ID retrieved successfully",
		zap.Uint("admin_user_id", currentUser.ID),
		zap.Uint("target_user_id", uint(userID)),
	)

	utils.RespondWithJSON(w, http.StatusOK, stats)
}

// UpdatePersonalBest обновляет личный рекорд пользователя
func (h *UserStatsHandler) UpdatePersonalBest(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling update personal best request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Получаем пользователя из контекста
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("update personal best failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	var req struct {
		DistanceType string `json:"distance_type"`
		Time         string `json:"time"`
	}

	if err := utils.DecodeJSONBody(w, r, &req); err != nil {
		h.logger.Error("failed to decode update personal best request",
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
		return
	}

	// Валидация обязательных полей
	if req.DistanceType == "" || req.Time == "" {
		h.logger.Warn("update personal best failed - missing required fields")
		utils.RespondWithError(w, http.StatusBadRequest, "Distance type and time are required")
		return
	}

	// Валидация типа дистанции
	validDistanceTypes := map[string]bool{
		"5k": true, "10k": true, "half": true, "marathon": true,
	}
	if !validDistanceTypes[req.DistanceType] {
		h.logger.Warn("update personal best failed - invalid distance type",
			zap.String("distance_type", req.DistanceType),
		)
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid distance type. Must be: 5k, 10k, half, marathon")
		return
	}

	h.logger.Info("updating personal best",
		zap.Uint("user_id", user.ID),
		zap.String("distance_type", req.DistanceType),
		zap.String("time", req.Time),
	)

	// Обновляем личный рекорд через сервис
	if err := h.userStatsService.UpdatePersonalBest(user.ID, req.DistanceType, req.Time); err != nil {
		h.logger.Error("failed to update personal best in service",
			zap.Uint("user_id", user.ID),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update personal best: "+err.Error())
		return
	}

	h.logger.Info("personal best updated successfully",
		zap.Uint("user_id", user.ID),
		zap.String("distance_type", req.DistanceType),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
		"message": "Personal best updated successfully",
		"distance_type": req.DistanceType,
		"time": req.Time,
	})
}

// IncrementWorkout увеличивает счетчик тренировок и обновляет статистику
func (h *UserStatsHandler) IncrementWorkout(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling increment workout request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Получаем пользователя из контекста
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("increment workout failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	var req struct {
		Distance float64 `json:"distance"`
		Duration int     `json:"duration"`
	}

	if err := utils.DecodeJSONBody(w, r, &req); err != nil {
		h.logger.Error("failed to decode increment workout request",
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
		return
	}

	// Валидация данных тренировки
	if req.Distance <= 0 {
		h.logger.Warn("increment workout failed - invalid distance",
			zap.Float64("distance", req.Distance),
		)
		utils.RespondWithError(w, http.StatusBadRequest, "Distance must be greater than 0")
		return
	}
	if req.Duration <= 0 {
		h.logger.Warn("increment workout failed - invalid duration",
			zap.Int("duration", req.Duration),
		)
		utils.RespondWithError(w, http.StatusBadRequest, "Duration must be greater than 0")
		return
	}

	h.logger.Info("incrementing workout stats",
		zap.Uint("user_id", user.ID),
		zap.Float64("distance", req.Distance),
		zap.Int("duration", req.Duration),
	)

	// Обновляем статистику через сервис
	if err := h.userStatsService.IncrementWorkout(user.ID, req.Distance, req.Duration); err != nil {
		h.logger.Error("failed to increment workout in service",
			zap.Uint("user_id", user.ID),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update workout stats: "+err.Error())
		return
	}

	h.logger.Info("workout stats incremented successfully",
		zap.Uint("user_id", user.ID),
		zap.Float64("distance", req.Distance),
		zap.Int("duration", req.Duration),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
		"message": "Workout stats updated successfully",
		"distance": req.Distance,
		"duration": req.Duration,
	})
}

// ResetWeeklyDistance сбрасывает недельный пробег
func (h *UserStatsHandler) ResetWeeklyDistance(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling reset weekly distance request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Получаем пользователя из контекста
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("reset weekly distance failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	h.logger.Info("resetting weekly distance",
		zap.Uint("user_id", user.ID),
	)

	// Сбрасываем недельный пробег через сервис
	if err := h.userStatsService.ResetWeeklyDistance(user.ID); err != nil {
		h.logger.Error("failed to reset weekly distance in service",
			zap.Uint("user_id", user.ID),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to reset weekly distance: "+err.Error())
		return
	}

	h.logger.Info("weekly distance reset successfully",
		zap.Uint("user_id", user.ID),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
		"message": "Weekly distance reset successfully",
	})
}

// ResetMonthlyDistance сбрасывает месячный пробег
func (h *UserStatsHandler) ResetMonthlyDistance(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling reset monthly distance request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Получаем пользователя из контекста
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("reset monthly distance failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	h.logger.Info("resetting monthly distance",
		zap.Uint("user_id", user.ID),
	)

	// Сбрасываем месячный пробег через сервис
	if err := h.userStatsService.ResetMonthlyDistance(user.ID); err != nil {
		h.logger.Error("failed to reset monthly distance in service",
			zap.Uint("user_id", user.ID),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to reset monthly distance: "+err.Error())
		return
	}

	h.logger.Info("monthly distance reset successfully",
		zap.Uint("user_id", user.ID),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
		"message": "Monthly distance reset successfully",
	})
}