package api import ( "database/sql" "net/http" "training-tracker/internal/models" ) type ExerciseHandler struct { repo ExerciseRepository } func NewExerciseHandler(repo ExerciseRepository) *ExerciseHandler { return &ExerciseHandler{repo: repo} } func (h *ExerciseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { id, _ := parseID(r, "/api/exercises/") switch r.Method { case http.MethodGet: if id > 0 { h.getByID(w, r, id) } else { h.list(w, r) } case http.MethodPost: h.create(w, r) case http.MethodPut: if id > 0 { h.update(w, r, id) } else { respondError(w, http.StatusBadRequest, "exercise ID required") } case http.MethodDelete: if id > 0 { h.delete(w, r, id) } else { respondError(w, http.StatusBadRequest, "exercise ID required") } default: respondError(w, http.StatusMethodNotAllowed, "method not allowed") } } func (h *ExerciseHandler) list(w http.ResponseWriter, r *http.Request) { exercises, err := h.repo.List(r.Context()) if err != nil { respondError(w, http.StatusInternalServerError, "failed to list exercises") return } if exercises == nil { exercises = []models.Exercise{} } respondJSON(w, http.StatusOK, exercises) } func (h *ExerciseHandler) getByID(w http.ResponseWriter, r *http.Request, id int64) { exercise, err := h.repo.GetByID(r.Context(), id) if err != nil { respondError(w, http.StatusInternalServerError, "failed to get exercise") return } if exercise == nil { respondError(w, http.StatusNotFound, "exercise not found") return } respondJSON(w, http.StatusOK, exercise) } func (h *ExerciseHandler) create(w http.ResponseWriter, r *http.Request) { var req models.CreateExerciseRequest if err := decodeJSON(r, &req); err != nil { respondError(w, http.StatusBadRequest, "invalid request body") return } if req.Name == "" { respondError(w, http.StatusBadRequest, "name is required") return } if req.Type != models.ExerciseTypeStrength && req.Type != models.ExerciseTypeCardio { respondError(w, http.StatusBadRequest, "type must be 'strength' or 'cardio'") return } exercise, err := h.repo.Create(r.Context(), &req) if err != nil { respondError(w, http.StatusInternalServerError, "failed to create exercise") return } respondJSON(w, http.StatusCreated, exercise) } func (h *ExerciseHandler) update(w http.ResponseWriter, r *http.Request, id int64) { var req models.CreateExerciseRequest if err := decodeJSON(r, &req); err != nil { respondError(w, http.StatusBadRequest, "invalid request body") return } if req.Name == "" { respondError(w, http.StatusBadRequest, "name is required") return } if req.Type != models.ExerciseTypeStrength && req.Type != models.ExerciseTypeCardio { respondError(w, http.StatusBadRequest, "type must be 'strength' or 'cardio'") return } exercise, err := h.repo.Update(r.Context(), id, &req) if err != nil { respondError(w, http.StatusInternalServerError, "failed to update exercise") return } if exercise == nil { respondError(w, http.StatusNotFound, "exercise not found") return } respondJSON(w, http.StatusOK, exercise) } func (h *ExerciseHandler) delete(w http.ResponseWriter, r *http.Request, id int64) { err := h.repo.Delete(r.Context(), id) if err == sql.ErrNoRows { respondError(w, http.StatusNotFound, "exercise not found") return } if err != nil { respondError(w, http.StatusInternalServerError, "failed to delete exercise") return } w.WriteHeader(http.StatusNoContent) }