// handlers/user_achievement_handler.go
package handlers

import (
	"net/http"
	"strconv"

	"api_bb/internal/models"
	"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 UserAchievementHandler struct {
	logger             logger.LoggerInterface
	achievementService service.AchievementService
}

func NewUserAchievementHandler(achievementService service.AchievementService) *UserAchievementHandler {
	return &UserAchievementHandler{
		logger:             logger.NewWrapper(logger.Get().With(zap.String("handler", "user_achievement"))),
		achievementService: achievementService,
	}
}

// GetAchievementsByType возвращает достижения по типу
func (h *UserAchievementHandler) GetAchievementsByType(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get achievements by type 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 achievements by type failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Получаем тип из URL параметров
	achievementType := chi.URLParam(r, "type")
	if achievementType == "" {
		utils.RespondWithError(w, http.StatusBadRequest, "Achievement type is required")
		return
	}

	// Валидируем тип достижения
	validType := models.AchievementType(achievementType)
	switch validType {
	case models.AchievementTypeDistance, models.AchievementTypeSpeed,
		models.AchievementTypeConsistency, models.AchievementTypeEvent,
		models.AchievementTypeSpecial:
		// valid type
	default:
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid achievement type")
		return
	}

	achievements, err := h.achievementService.GetAchievementsByType(user.ID, validType)
	if err != nil {
		h.logger.Error("failed to get achievements by type",
			zap.Uint("user_id", user.ID),
			zap.String("type", achievementType),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievements: "+err.Error())
		return
	}

	h.logger.Info("achievements by type retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.String("type", achievementType),
		zap.Int("achievements_count", len(achievements)),
	)

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

// GetAchievementByID возвращает достижение по ID
func (h *UserAchievementHandler) GetAchievementByID(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get achievement by ID 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 achievement by ID failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Получаем ID достижения из URL параметров
	achievementIDStr := chi.URLParam(r, "id")
	if achievementIDStr == "" {
		utils.RespondWithError(w, http.StatusBadRequest, "Achievement ID is required")
		return
	}

	achievementID, err := strconv.ParseUint(achievementIDStr, 10, 32)
	if err != nil {
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid achievement ID")
		return
	}

	achievement, err := h.achievementService.GetAchievementByID(uint(achievementID), user.ID)
	if err != nil {
		h.logger.Error("failed to get achievement by ID",
			zap.Uint("user_id", user.ID),
			zap.Uint("achievement_id", uint(achievementID)),
			zap.Error(err),
		)
		if err == service.ErrAchievementNotFound {
			utils.RespondWithError(w, http.StatusNotFound, "Achievement not found")
		} else {
			utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievement: "+err.Error())
		}
		return
	}

	h.logger.Info("achievement retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("achievement_id", uint(achievementID)),
	)

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

// UpdateAchievement обновляет достижение
func (h *UserAchievementHandler) UpdateAchievement(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling update achievement 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 achievement failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Получаем ID достижения из URL параметров
	achievementIDStr := chi.URLParam(r, "id")
	if achievementIDStr == "" {
		utils.RespondWithError(w, http.StatusBadRequest, "Achievement ID is required")
		return
	}

	achievementID, err := strconv.ParseUint(achievementIDStr, 10, 32)
	if err != nil {
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid achievement ID")
		return
	}

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

	// Валидация запроса
	if err := utils.ValidateStruct(req); err != nil {
		h.logger.Warn("achievement update validation failed", zap.Error(err))
		utils.RespondWithValidationError(w, err)
		return
	}

	// Обновляем достижение через сервис
	achievement, err := h.achievementService.UpdateAchievement(uint(achievementID), user.ID, req)
	if err != nil {
		h.logger.Error("failed to update achievement",
			zap.Uint("user_id", user.ID),
			zap.Uint("achievement_id", uint(achievementID)),
			zap.Error(err),
		)
		switch err {
		case service.ErrAchievementNotFound:
			utils.RespondWithError(w, http.StatusNotFound, "Achievement not found")
		case service.ErrAchievementAlreadyExists:
			utils.RespondWithError(w, http.StatusConflict, "Achievement with this title already exists")
		default:
			utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update achievement: "+err.Error())
		}
		return
	}

	h.logger.Info("achievement updated successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("achievement_id", uint(achievementID)),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
		"message":     "Achievement updated successfully",
		"achievement": achievement,
	})
}

// GetPublicUserAchievements возвращает достижения пользователя для публичного просмотра
func (h *UserAchievementHandler) GetPublicUserAchievements(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get public user achievements request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Получаем userID из URL параметров
	userIDStr := r.URL.Query().Get("userID")
	if userIDStr == "" {
		utils.RespondWithError(w, http.StatusBadRequest, "User ID is required")
		return
	}

	userID, err := strconv.ParseUint(userIDStr, 10, 32)
	if err != nil {
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid user ID")
		return
	}

	// Получаем только подтвержденные достижения для публичного просмотра
	achievements, err := h.achievementService.GetVerifiedAchievements(uint(userID))
	if err != nil {
		h.logger.Error("failed to get public user achievements",
			zap.Uint("user_id", uint(userID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievements: "+err.Error())
		return
	}

	h.logger.Info("public user achievements retrieved successfully",
		zap.Uint("user_id", uint(userID)),
		zap.Int("achievements_count", len(achievements)),
	)

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

// GetPublicUserAchievementsSummary возвращает сводку по достижениям пользователя для публичного просмотра
func (h *UserAchievementHandler) GetPublicUserAchievementsSummary(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get public user achievements summary request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Получаем userID из URL параметров
	userIDStr := r.URL.Query().Get("userID")
	if userIDStr == "" {
		utils.RespondWithError(w, http.StatusBadRequest, "User ID is required")
		return
	}

	userID, err := strconv.ParseUint(userIDStr, 10, 32)
	if err != nil {
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid user ID")
		return
	}

	summary, err := h.achievementService.GetUserAchievementsSummary(uint(userID))
	if err != nil {
		h.logger.Error("failed to get public user achievements summary",
			zap.Uint("user_id", uint(userID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievements summary: "+err.Error())
		return
	}

	h.logger.Info("public user achievements summary retrieved successfully",
		zap.Uint("user_id", uint(userID)),
		zap.Int("total_achievements", summary.TotalAchievements),
		zap.Int("completed", summary.Completed),
	)

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

// GetPublicRecentAchievements возвращает последние достижения пользователя для публичного просмотра
func (h *UserAchievementHandler) GetPublicRecentAchievements(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get public recent achievements request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Получаем userID из URL параметров
	userIDStr := r.URL.Query().Get("userID")
	if userIDStr == "" {
		utils.RespondWithError(w, http.StatusBadRequest, "User ID is required")
		return
	}

	userID, err := strconv.ParseUint(userIDStr, 10, 32)
	if err != nil {
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid user ID")
		return
	}

	// Получаем параметр limit из query string (по умолчанию 10)
	limit := 10
	limitStr := r.URL.Query().Get("limit")
	if limitStr != "" {
		if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 {
			limit = parsedLimit
		}
	}

	// Получаем только подтвержденные достижения
	achievements, err := h.achievementService.GetVerifiedRecentAchievements(uint(userID), limit)
	if err != nil {
		h.logger.Error("failed to get public recent achievements",
			zap.Uint("user_id", uint(userID)),
			zap.Int("limit", limit),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get recent achievements: "+err.Error())
		return
	}

	h.logger.Info("public recent achievements retrieved successfully",
		zap.Uint("user_id", uint(userID)),
		zap.Int("achievements_count", len(achievements)),
	)

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

// CreateAchievement создает новое достижение для пользователя
func (h *UserAchievementHandler) CreateAchievement(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling create achievement 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("create achievement failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

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

	// Валидация запроса
	if err := utils.ValidateStruct(req); err != nil {
		h.logger.Warn("achievement validation failed", zap.Error(err))
		utils.RespondWithValidationError(w, err)
		return
	}

	// Создаем достижение через сервис
	achievement, err := h.achievementService.CreateAchievement(user.ID, req)
	if err != nil {
		h.logger.Error("failed to create achievement",
			zap.Uint("user_id", user.ID),
			zap.Error(err),
		)
		if err == service.ErrAchievementAlreadyExists {
			utils.RespondWithError(w, http.StatusConflict, "Achievement with this title already exists")
		} else {
			utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create achievement: "+err.Error())
		}
		return
	}

	h.logger.Info("achievement created successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("achievement_id", achievement.ID),
		zap.String("title", achievement.Title),
	)

	utils.RespondWithJSON(w, http.StatusCreated, map[string]interface{}{
		"message":     "Achievement created successfully",
		"achievement": achievement,
	})
}

// GetUserAchievements возвращает все достижения пользователя
func (h *UserAchievementHandler) GetUserAchievements(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get user achievements 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 achievements failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	achievements, err := h.achievementService.GetUserAchievements(user.ID)
	if err != nil {
		h.logger.Error("failed to get user achievements",
			zap.Uint("user_id", user.ID),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievements: "+err.Error())
		return
	}

	h.logger.Info("user achievements retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.Int("achievements_count", len(achievements)),
	)

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

// GetUserAchievementsSummary возвращает сводку по достижениям пользователя
func (h *UserAchievementHandler) GetUserAchievementsSummary(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get user achievements summary 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 achievements summary failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	summary, err := h.achievementService.GetUserAchievementsSummary(user.ID)
	if err != nil {
		h.logger.Error("failed to get user achievements summary",
			zap.Uint("user_id", user.ID),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievements summary: "+err.Error())
		return
	}

	h.logger.Info("user achievements summary retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.Int("total_achievements", summary.TotalAchievements),
		zap.Int("completed", summary.Completed),
		zap.Float64("progress_percent", summary.ProgressPercent),
	)

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

// GetRecentAchievements возвращает последние достижения пользователя
func (h *UserAchievementHandler) GetRecentAchievements(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get recent achievements 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 recent achievements failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Получаем параметр limit из query string (по умолчанию 10)
	limit := 10
	limitStr := r.URL.Query().Get("limit")
	if limitStr != "" {
		if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 {
			limit = parsedLimit
		}
	}

	achievements, err := h.achievementService.GetRecentAchievements(user.ID, limit)
	if err != nil {
		h.logger.Error("failed to get recent achievements",
			zap.Uint("user_id", user.ID),
			zap.Int("limit", limit),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get recent achievements: "+err.Error())
		return
	}

	h.logger.Info("recent achievements retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.Int("achievements_count", len(achievements)),
		zap.Int("limit", limit),
	)

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

// VerifyAchievement подтверждает достижение пользователя
func (h *UserAchievementHandler) VerifyAchievement(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling verify achievement 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("verify achievement failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Получаем ID достижения из URL параметров
	achievementIDStr := r.URL.Query().Get("id")
	if achievementIDStr == "" {
		utils.RespondWithError(w, http.StatusBadRequest, "Achievement ID is required")
		return
	}

	achievementID, err := strconv.ParseUint(achievementIDStr, 10, 32)
	if err != nil {
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid achievement ID")
		return
	}

	err = h.achievementService.VerifyAchievement(uint(achievementID), user.ID)
	if err != nil {
		h.logger.Error("failed to verify achievement",
			zap.Uint("user_id", user.ID),
			zap.Uint("achievement_id", uint(achievementID)),
			zap.Error(err),
		)
		if err == service.ErrAchievementNotFound {
			utils.RespondWithError(w, http.StatusNotFound, "Achievement not found")
		} else {
			utils.RespondWithError(w, http.StatusInternalServerError, "Failed to verify achievement: "+err.Error())
		}
		return
	}

	h.logger.Info("achievement verified successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("achievement_id", uint(achievementID)),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]string{
		"message": "Achievement verified successfully",
	})
}

// DeleteAchievement удаляет достижение пользователя
func (h *UserAchievementHandler) DeleteAchievement(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling delete achievement 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("delete achievement failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Получаем ID достижения из URL параметров
	achievementIDStr := r.URL.Query().Get("id")
	if achievementIDStr == "" {
		utils.RespondWithError(w, http.StatusBadRequest, "Achievement ID is required")
		return
	}

	achievementID, err := strconv.ParseUint(achievementIDStr, 10, 32)
	if err != nil {
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid achievement ID")
		return
	}

	err = h.achievementService.DeleteAchievement(uint(achievementID), user.ID)
	if err != nil {
		h.logger.Error("failed to delete achievement",
			zap.Uint("user_id", user.ID),
			zap.Uint("achievement_id", uint(achievementID)),
			zap.Error(err),
		)
		if err == service.ErrAchievementNotFound {
			utils.RespondWithError(w, http.StatusNotFound, "Achievement not found")
		} else {
			utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete achievement: "+err.Error())
		}
		return
	}

	h.logger.Info("achievement deleted successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("achievement_id", uint(achievementID)),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]string{
		"message": "Achievement deleted successfully",
	})
}
