// handlers/personal_best_handler.go
package handlers

import (
	"encoding/json"
	"net/http"
	"strconv"

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

	"go.uber.org/zap"

	"github.com/go-chi/chi/v5"
)

type PersonalBestHandler struct {
	logger              logger.LoggerInterface
	personalBestService service.PersonalBestService
}

func NewPersonalBestHandler(personalBestService service.PersonalBestService) *PersonalBestHandler {
	return &PersonalBestHandler{
		logger:              logger.NewWrapper(logger.Get().With(zap.String("handler", "personal_best"))),
		personalBestService: personalBestService,
	}
}

// CreatePersonalBest создает новый личный рекорд
func (h *PersonalBestHandler) CreatePersonalBest(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling create 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("create personal best failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

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

	// Валидация
	if req.DistanceType == "" {
		h.logger.Warn("create personal best failed - distance type required")
		utils.RespondWithError(w, http.StatusBadRequest, "Distance type is required")
		return
	}
	if req.Time == "" {
		h.logger.Warn("create personal best failed - time required")
		utils.RespondWithError(w, http.StatusBadRequest, "Time is required")
		return
	}
	if req.Date.IsZero() {
		h.logger.Warn("create personal best failed - date required")
		utils.RespondWithError(w, http.StatusBadRequest, "Date is required")
		return
	}

	personalBest, err := h.personalBestService.CreatePersonalBest(user.ID, req)
	if err != nil {
		h.logger.Error("failed to create personal best", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create personal best: "+err.Error())
		return
	}

	h.logger.Info("personal best created successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("personal_best_id", personalBest.ID),
		zap.String("distance_type", string(personalBest.DistanceType)),
	)

	utils.RespondWithJSON(w, http.StatusCreated, personalBest)
}

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

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.logger.Warn("invalid personal best ID", zap.String("id", idStr))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid personal best ID")
		return
	}

	personalBest, err := h.personalBestService.GetPersonalBestByID(uint(id))
	if err != nil {
		h.logger.Error("failed to get personal best", zap.Error(err))
		if err.Error() == "record not found" {
			utils.RespondWithError(w, http.StatusNotFound, "Personal best not found")
		} else {
			utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get personal best: "+err.Error())
		}
		return
	}

	h.logger.Info("personal best retrieved successfully",
		zap.Uint("personal_best_id", personalBest.ID),
	)

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

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

	personalBests, err := h.personalBestService.GetUserPersonalBests(user.ID)
	if err != nil {
		h.logger.Error("failed to get personal bests", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get personal bests: "+err.Error())
		return
	}

	h.logger.Info("user personal bests retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.Int("count", len(personalBests)),
	)

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

// UpdatePersonalBest обновляет личный рекорд
func (h *PersonalBestHandler) 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
	}

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.logger.Warn("invalid personal best ID", zap.String("id", idStr))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid personal best ID")
		return
	}

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

	personalBest, err := h.personalBestService.UpdatePersonalBest(uint(id), user.ID, req)
	if err != nil {
		h.logger.Error("failed to update personal best", zap.Error(err))
		if err.Error() == "record not found" {
			utils.RespondWithError(w, http.StatusNotFound, "Personal best not found or access denied")
		} else {
			utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update personal best: "+err.Error())
		}
		return
	}

	h.logger.Info("personal best updated successfully",
		zap.Uint("personal_best_id", personalBest.ID),
		zap.Uint("user_id", user.ID),
	)

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

// DeletePersonalBest удаляет личный рекорд
func (h *PersonalBestHandler) DeletePersonalBest(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling delete 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("delete personal best failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.logger.Warn("invalid personal best ID", zap.String("id", idStr))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid personal best ID")
		return
	}

	err = h.personalBestService.DeletePersonalBest(uint(id), user.ID)
	if err != nil {
		h.logger.Error("failed to delete personal best", zap.Error(err))
		if err.Error() == "record not found" {
			utils.RespondWithError(w, http.StatusNotFound, "Personal best not found or access denied")
		} else {
			utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete personal best: "+err.Error())
		}
		return
	}

	h.logger.Info("personal best deleted successfully",
		zap.Uint("personal_best_id", uint(id)),
		zap.Uint("user_id", user.ID),
	)

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

// GetPersonalBestsByDistance возвращает личные рекорды по дистанции
func (h *PersonalBestHandler) GetPersonalBestsByDistance(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get personal bests by 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("get personal bests by distance failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	distanceType := models.DistanceType(chi.URLParam(r, "distanceType"))
	if distanceType == "" {
		h.logger.Warn("distance type parameter is required")
		utils.RespondWithError(w, http.StatusBadRequest, "Distance type parameter is required")
		return
	}

	// Валидация типа дистанции
	validDistances := map[models.DistanceType]bool{
		models.Distance5K:    true,
		models.Distance10K:   true,
		models.DistanceHalf:  true,
		models.DistanceFull:  true,
		models.DistanceOther: true,
	}

	if !validDistances[distanceType] {
		h.logger.Warn("invalid distance type", zap.String("distance_type", string(distanceType)))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid distance type")
		return
	}

	personalBests, err := h.personalBestService.GetPersonalBestsByDistance(user.ID, distanceType)
	if err != nil {
		h.logger.Error("failed to get personal bests by distance", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get personal bests: "+err.Error())
		return
	}

	h.logger.Info("personal bests by distance retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.String("distance_type", string(distanceType)),
		zap.Int("count", len(personalBests)),
	)

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

// GetBestByDistance возвращает лучший результат на дистанции
func (h *PersonalBestHandler) GetBestByDistance(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get best by 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("get best by distance failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	distanceType := models.DistanceType(chi.URLParam(r, "distanceType"))
	if distanceType == "" {
		h.logger.Warn("distance type parameter is required")
		utils.RespondWithError(w, http.StatusBadRequest, "Distance type parameter is required")
		return
	}

	best, err := h.personalBestService.GetBestByDistance(user.ID, distanceType)
	if err != nil {
		if err.Error() == "record not found" {
			h.logger.Info("no personal best found for distance",
				zap.Uint("user_id", user.ID),
				zap.String("distance_type", string(distanceType)),
			)
			utils.RespondWithJSON(w, http.StatusOK, nil)
			return
		}
		h.logger.Error("failed to get best by distance", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get best result: "+err.Error())
		return
	}

	h.logger.Info("best by distance retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.String("distance_type", string(distanceType)),
	)

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

// GetPersonalBestsSummary возвращает сводку лучших результатов
func (h *PersonalBestHandler) GetPersonalBestsSummary(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get personal bests 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 personal bests summary failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	summary, err := h.personalBestService.GetPersonalBestsSummary(user.ID)
	if err != nil {
		h.logger.Error("failed to get personal bests summary", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get personal bests summary: "+err.Error())
		return
	}

	h.logger.Info("personal bests summary retrieved successfully",
		zap.Uint("user_id", user.ID),
	)

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

// VerifyPersonalBest подтверждает личный рекорд
func (h *PersonalBestHandler) VerifyPersonalBest(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling verify 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("verify personal best failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.logger.Warn("invalid personal best ID", zap.String("id", idStr))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid personal best ID")
		return
	}

	err = h.personalBestService.VerifyPersonalBest(uint(id), user.ID)
	if err != nil {
		h.logger.Error("failed to verify personal best", zap.Error(err))
		if err.Error() == "record not found" {
			utils.RespondWithError(w, http.StatusNotFound, "Personal best not found or access denied")
		} else {
			utils.RespondWithError(w, http.StatusInternalServerError, "Failed to verify personal best: "+err.Error())
		}
		return
	}

	h.logger.Info("personal best verified successfully",
		zap.Uint("personal_best_id", uint(id)),
		zap.Uint("user_id", user.ID),
	)

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

// GetRecentPersonalBests возвращает последние личные рекорды
func (h *PersonalBestHandler) GetRecentPersonalBests(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get recent personal bests 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 personal bests failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	limit := 10 // default limit
	limitStr := r.URL.Query().Get("limit")
	if limitStr != "" {
		if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
			limit = l
		}
	}

	personalBests, err := h.personalBestService.GetRecentPersonalBests(user.ID, limit)
	if err != nil {
		h.logger.Error("failed to get recent personal bests", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get recent personal bests: "+err.Error())
		return
	}

	h.logger.Info("recent personal bests retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.Int("limit", limit),
		zap.Int("count", len(personalBests)),
	)

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

// CalculatePace вычисляет темп
func (h *PersonalBestHandler) CalculatePace(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling calculate pace request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

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

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

	if req.Time == "" || req.DistanceType == "" {
		h.logger.Warn("time and distance type are required")
		utils.RespondWithError(w, http.StatusBadRequest, "Time and distance type are required")
		return
	}

	pace, err := h.personalBestService.CalculatePace(req.Time, req.DistanceType)
	if err != nil {
		h.logger.Error("failed to calculate pace", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Failed to calculate pace: "+err.Error())
		return
	}

	h.logger.Info("pace calculated successfully",
		zap.String("time", req.Time),
		zap.String("distance_type", string(req.DistanceType)),
		zap.String("pace", pace),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
		"time":          req.Time,
		"distance_type": req.DistanceType,
		"pace":          pace,
	})
}