// handlers/training_plan_handler.go
package handlers

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

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

	"go.uber.org/zap"
)

type TrainingPlanHandler struct {
	logger            logger.LoggerInterface
	trainingPlanService service.TrainingPlanService
}

func NewTrainingPlanHandler(trainingPlanService service.TrainingPlanService) *TrainingPlanHandler {
	return &TrainingPlanHandler{
		logger:            logger.NewWrapper(logger.Get().With(zap.String("handler", "training_plan"))),
		trainingPlanService: trainingPlanService,
	}
}

// TrainingPlanResponse - DTO для ответа с планом тренировок
type TrainingPlanResponse struct {
	ID              uint                   `json:"id"`
	UserID          uint                   `json:"user_id"`
	Title           string                 `json:"title"`
	Description     string                 `json:"description"`
	Weeks           int                    `json:"weeks"`
	WorkoutsPerWeek int                    `json:"workouts_per_week"`
	TargetDistance  string                 `json:"target_distance"`
	TargetDate      time.Time              `json:"target_date"`
	CurrentWeek     int                    `json:"current_week"`
	Completed       bool                   `json:"completed"`
	CreatedAt       time.Time              `json:"created_at"`
	UpdatedAt       time.Time              `json:"updated_at"`
	Workouts        []TrainingWorkoutResponse `json:"workouts,omitempty"`
}

// TrainingWorkoutResponse - DTO для ответа с тренировкой плана
type TrainingWorkoutResponse struct {
	ID          uint        `json:"id"`
	PlanID      uint        `json:"plan_id"`
	Week        int         `json:"week"`
	Day         int         `json:"day"`
	Type        models.WorkoutType `json:"type"`
	Description string      `json:"description"`
	Distance    float64     `json:"distance_km"`
	Duration    int         `json:"duration_min"`
	Completed   bool        `json:"completed"`
	CompletedAt *time.Time  `json:"completed_at"`
	CreatedAt   time.Time   `json:"created_at"`
}

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

	var req models.TrainingPlanCreateRequest
	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
	}

	h.logger.Debug("creating training plan",
		zap.Uint("user_id", user.ID),
		zap.String("title", req.Title),
		zap.Int("weeks", req.Weeks),
		zap.Int("workouts_per_week", req.WorkoutsPerWeek),
	)

	// Создаем план тренировок через сервис
	plan, err := h.trainingPlanService.CreateTrainingPlan(user.ID, &req)
	if err != nil {
		h.logger.Error("failed to create training plan in service",
			zap.Uint("user_id", user.ID),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create training plan: "+err.Error())
		return
	}

	h.logger.Info("training plan created successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", plan.ID),
	)

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

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

	h.logger.Debug("getting training plans for user", zap.Uint("user_id", user.ID))

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

	// Преобразуем в response формат
	var planResponses []TrainingPlanResponse
	for _, plan := range plans {
		planResponses = append(planResponses, toTrainingPlanResponse(&plan))
	}

	h.logger.Debug("training plans retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.Int("plans_count", len(planResponses)),
	)

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

// GetTrainingPlanByID возвращает план тренировок по ID
func (h *TrainingPlanHandler) GetTrainingPlanByID(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("handling get training plan 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 training plan failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Извлекаем ID плана из URL параметров
	planIDStr := r.URL.Query().Get("id")
	if planIDStr == "" {
		h.logger.Warn("get training plan failed - plan ID required")
		utils.RespondWithError(w, http.StatusBadRequest, "Plan ID is required")
		return
	}

	planID, err := strconv.ParseUint(planIDStr, 10, 32)
	if err != nil {
		h.logger.Warn("get training plan failed - invalid plan ID",
			zap.String("plan_id", planIDStr),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid plan ID")
		return
	}

	h.logger.Debug("getting training plan by ID",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", uint(planID)),
	)

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

	h.logger.Debug("training plan retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", uint(planID)),
	)

	utils.RespondWithJSON(w, http.StatusOK, toTrainingPlanResponse(plan))
}

// UpdateTrainingPlan обновляет план тренировок
func (h *TrainingPlanHandler) UpdateTrainingPlan(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling update training plan 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 training plan failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Извлекаем ID плана из URL параметров
	planIDStr := r.URL.Query().Get("id")
	if planIDStr == "" {
		h.logger.Warn("update training plan failed - plan ID required")
		utils.RespondWithError(w, http.StatusBadRequest, "Plan ID is required")
		return
	}

	planID, err := strconv.ParseUint(planIDStr, 10, 32)
	if err != nil {
		h.logger.Warn("update training plan failed - invalid plan ID",
			zap.String("plan_id", planIDStr),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid plan ID")
		return
	}

	var req models.TrainingPlanUpdateRequest
	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
	}

	h.logger.Info("updating training plan",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", uint(planID)),
		zap.String("title", req.Title),
	)

	// Обновляем план тренировок через сервис
	plan, err := h.trainingPlanService.UpdateTrainingPlan(user.ID, uint(planID), &req)
	if err != nil {
		h.logger.Error("failed to update training plan in service",
			zap.Uint("user_id", user.ID),
			zap.Uint("plan_id", uint(planID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update training plan: "+err.Error())
		return
	}

	h.logger.Info("training plan updated successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", uint(planID)),
	)

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

// DeleteTrainingPlan удаляет план тренировок
func (h *TrainingPlanHandler) DeleteTrainingPlan(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling delete training plan 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 training plan failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Извлекаем ID плана из URL параметров
	planIDStr := r.URL.Query().Get("id")
	if planIDStr == "" {
		h.logger.Warn("delete training plan failed - plan ID required")
		utils.RespondWithError(w, http.StatusBadRequest, "Plan ID is required")
		return
	}

	planID, err := strconv.ParseUint(planIDStr, 10, 32)
	if err != nil {
		h.logger.Warn("delete training plan failed - invalid plan ID",
			zap.String("plan_id", planIDStr),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid plan ID")
		return
	}

	h.logger.Info("deleting training plan",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", uint(planID)),
	)

	// Удаляем план тренировок через сервис
	if err := h.trainingPlanService.DeleteTrainingPlan(user.ID, uint(planID)); err != nil {
		h.logger.Error("failed to delete training plan in service",
			zap.Uint("user_id", user.ID),
			zap.Uint("plan_id", uint(planID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete training plan: "+err.Error())
		return
	}

	h.logger.Info("training plan deleted successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", uint(planID)),
	)

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

// GetActiveTrainingPlan возвращает активный план тренировок пользователя
func (h *TrainingPlanHandler) GetActiveTrainingPlan(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("handling get active training plan 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 active training plan failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	h.logger.Debug("getting active training plan for user", zap.Uint("user_id", user.ID))

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

	h.logger.Debug("active training plan retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", plan.ID),
	)

	utils.RespondWithJSON(w, http.StatusOK, toTrainingPlanResponse(plan))
}

// MarkTrainingPlanAsCompleted помечает план тренировок как завершенный
func (h *TrainingPlanHandler) MarkTrainingPlanAsCompleted(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling mark training plan as completed 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("mark training plan as completed failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Извлекаем ID плана из URL параметров
	planIDStr := r.URL.Query().Get("id")
	if planIDStr == "" {
		h.logger.Warn("mark training plan as completed failed - plan ID required")
		utils.RespondWithError(w, http.StatusBadRequest, "Plan ID is required")
		return
	}

	planID, err := strconv.ParseUint(planIDStr, 10, 32)
	if err != nil {
		h.logger.Warn("mark training plan as completed failed - invalid plan ID",
			zap.String("plan_id", planIDStr),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid plan ID")
		return
	}

	h.logger.Info("marking training plan as completed",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", uint(planID)),
	)

	// Помечаем план как завершенный через сервис
	if err := h.trainingPlanService.MarkTrainingPlanAsCompleted(user.ID, uint(planID)); err != nil {
		h.logger.Error("failed to mark training plan as completed in service",
			zap.Uint("user_id", user.ID),
			zap.Uint("plan_id", uint(planID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to mark training plan as completed: "+err.Error())
		return
	}

	h.logger.Info("training plan marked as completed successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", uint(planID)),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
		"message": "Training plan marked as completed successfully",
	})
}

// UpdateCurrentWeek обновляет текущую неделю плана тренировок
func (h *TrainingPlanHandler) UpdateCurrentWeek(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling update current week 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 current week failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Извлекаем ID плана из URL параметров
	planIDStr := r.URL.Query().Get("id")
	if planIDStr == "" {
		h.logger.Warn("update current week failed - plan ID required")
		utils.RespondWithError(w, http.StatusBadRequest, "Plan ID is required")
		return
	}

	planID, err := strconv.ParseUint(planIDStr, 10, 32)
	if err != nil {
		h.logger.Warn("update current week failed - invalid plan ID",
			zap.String("plan_id", planIDStr),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid plan ID")
		return
	}

	var req struct {
		CurrentWeek int `json:"current_week" validate:"required,min=1,max=52"`
	}
	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
	}

	h.logger.Info("updating current week for training plan",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", uint(planID)),
		zap.Int("current_week", req.CurrentWeek),
	)

	// Обновляем текущую неделю через сервис
	if err := h.trainingPlanService.UpdateCurrentWeek(user.ID, uint(planID), req.CurrentWeek); err != nil {
		h.logger.Error("failed to update current week in service",
			zap.Uint("user_id", user.ID),
			zap.Uint("plan_id", uint(planID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update current week: "+err.Error())
		return
	}

	h.logger.Info("current week updated successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("plan_id", uint(planID)),
		zap.Int("current_week", req.CurrentWeek),
	)

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

// Вспомогательные функции для преобразования моделей в DTO

func toTrainingPlanResponse(plan *models.TrainingPlan) TrainingPlanResponse {
	response := TrainingPlanResponse{
		ID:              plan.ID,
		UserID:          plan.UserID,
		Title:           plan.Title,
		Description:     plan.Description,
		Weeks:           plan.Weeks,
		WorkoutsPerWeek: plan.WorkoutsPerWeek,
		TargetDistance:  plan.TargetDistance,
		TargetDate:      plan.TargetDate,
		CurrentWeek:     plan.CurrentWeek,
		Completed:       plan.Completed,
		CreatedAt:       plan.CreatedAt,
		UpdatedAt:       plan.UpdatedAt,
	}

	// Преобразуем тренировки, если они загружены
	if plan.Workouts != nil {
		for _, workout := range plan.Workouts {
			response.Workouts = append(response.Workouts, toTrainingWorkoutResponse(&workout))
		}
	}

	return response
}

func toTrainingWorkoutResponse(workout *models.TrainingWorkout) TrainingWorkoutResponse {
	return TrainingWorkoutResponse{
		ID:          workout.ID,
		PlanID:      workout.PlanID,
		Week:        workout.Week,
		Day:         workout.Day,
		Type:        workout.Type,
		Description: workout.Description,
		Distance:    workout.Distance,
		Duration:    workout.Duration,
		Completed:   workout.Completed,
		CompletedAt: workout.CompletedAt,
		CreatedAt:   workout.CreatedAt,
	}
}