package handler import ( "encoding/json" "errors" "net/http" "strconv" "time" "github.com/go-chi/chi/v5" "github.com/daniil/homelab-api/internal/middleware" "github.com/daniil/homelab-api/internal/model" "github.com/daniil/homelab-api/internal/repository" ) type HabitFreezeHandler struct { freezeRepo *repository.HabitFreezeRepository habitRepo *repository.HabitRepository } func NewHabitFreezeHandler(freezeRepo *repository.HabitFreezeRepository, habitRepo *repository.HabitRepository) *HabitFreezeHandler { return &HabitFreezeHandler{ freezeRepo: freezeRepo, habitRepo: habitRepo, } } func (h *HabitFreezeHandler) Create(w http.ResponseWriter, r *http.Request) { userID := middleware.GetUserID(r.Context()) habitID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { writeError(w, "invalid habit id", http.StatusBadRequest) return } // Verify habit exists and belongs to user if _, err := h.habitRepo.GetByID(habitID, userID); err != nil { if errors.Is(err, repository.ErrHabitNotFound) { writeError(w, "habit not found", http.StatusNotFound) return } writeError(w, "failed to fetch habit", http.StatusInternalServerError) return } var req model.CreateHabitFreezeRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, "invalid request body", http.StatusBadRequest) return } if req.StartDate == "" || req.EndDate == "" { writeError(w, "start_date and end_date are required", http.StatusBadRequest) return } startDate, err := time.Parse("2006-01-02", req.StartDate) if err != nil { writeError(w, "invalid start_date format", http.StatusBadRequest) return } endDate, err := time.Parse("2006-01-02", req.EndDate) if err != nil { writeError(w, "invalid end_date format", http.StatusBadRequest) return } freeze := &model.HabitFreeze{ HabitID: habitID, UserID: userID, StartDate: startDate, EndDate: endDate, Reason: req.Reason, } if err := h.freezeRepo.Create(freeze); err != nil { if errors.Is(err, repository.ErrInvalidDateRange) { writeError(w, "end_date must be after start_date", http.StatusBadRequest) return } writeError(w, "failed to create freeze", http.StatusInternalServerError) return } writeJSON(w, freeze, http.StatusCreated) } func (h *HabitFreezeHandler) List(w http.ResponseWriter, r *http.Request) { userID := middleware.GetUserID(r.Context()) habitID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { writeError(w, "invalid habit id", http.StatusBadRequest) return } // Verify habit exists and belongs to user if _, err := h.habitRepo.GetByID(habitID, userID); err != nil { if errors.Is(err, repository.ErrHabitNotFound) { writeError(w, "habit not found", http.StatusNotFound) return } writeError(w, "failed to fetch habit", http.StatusInternalServerError) return } freezes, err := h.freezeRepo.GetByHabitID(habitID, userID) if err != nil { writeError(w, "failed to fetch freezes", http.StatusInternalServerError) return } writeJSON(w, freezes, http.StatusOK) } func (h *HabitFreezeHandler) Delete(w http.ResponseWriter, r *http.Request) { userID := middleware.GetUserID(r.Context()) freezeID, err := strconv.ParseInt(chi.URLParam(r, "freezeId"), 10, 64) if err != nil { writeError(w, "invalid freeze id", http.StatusBadRequest) return } if err := h.freezeRepo.Delete(freezeID, userID); err != nil { if errors.Is(err, repository.ErrFreezeNotFound) { writeError(w, "freeze not found", http.StatusNotFound) return } writeError(w, "failed to delete freeze", http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) }