package api import ( "database/sql" "net/http" "strings" "training-tracker/internal/models" ) type SessionHandler struct { repo SessionRepository } func NewSessionHandler(repo SessionRepository) *SessionHandler { return &SessionHandler{repo: repo} } func (h *SessionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := strings.TrimPrefix(r.URL.Path, "/api/sessions") path = strings.TrimPrefix(path, "/") parts := strings.Split(path, "/") if len(parts) >= 2 && parts[1] == "entries" { id, err := parseID(r, "/api/sessions/") if err != nil || id == 0 { respondError(w, http.StatusBadRequest, "session ID required") return } if r.Method == http.MethodPost { h.addEntry(w, r, id) } else { respondError(w, http.StatusMethodNotAllowed, "method not allowed") } return } id, _ := parseID(r, "/api/sessions/") 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, "session ID required") } case http.MethodDelete: if id > 0 { h.delete(w, r, id) } else { respondError(w, http.StatusBadRequest, "session ID required") } default: respondError(w, http.StatusMethodNotAllowed, "method not allowed") } } func (h *SessionHandler) list(w http.ResponseWriter, r *http.Request) { sessions, err := h.repo.List(r.Context()) if err != nil { respondError(w, http.StatusInternalServerError, "failed to list sessions") return } if sessions == nil { sessions = []models.Session{} } respondJSON(w, http.StatusOK, sessions) } func (h *SessionHandler) getByID(w http.ResponseWriter, r *http.Request, id int64) { session, err := h.repo.GetByID(r.Context(), id) if err != nil { respondError(w, http.StatusInternalServerError, "failed to get session") return } if session == nil { respondError(w, http.StatusNotFound, "session not found") return } respondJSON(w, http.StatusOK, session) } func (h *SessionHandler) create(w http.ResponseWriter, r *http.Request) { var req models.CreateSessionRequest if err := decodeJSON(r, &req); err != nil { respondError(w, http.StatusBadRequest, "invalid request body") return } if req.Date.IsZero() { respondError(w, http.StatusBadRequest, "date is required") return } session, err := h.repo.Create(r.Context(), &req) if err != nil { respondError(w, http.StatusInternalServerError, "failed to create session") return } respondJSON(w, http.StatusCreated, session) } func (h *SessionHandler) update(w http.ResponseWriter, r *http.Request, id int64) { var req models.CreateSessionRequest if err := decodeJSON(r, &req); err != nil { respondError(w, http.StatusBadRequest, "invalid request body") return } if req.Date.IsZero() { respondError(w, http.StatusBadRequest, "date is required") return } session, err := h.repo.Update(r.Context(), id, &req) if err != nil { respondError(w, http.StatusInternalServerError, "failed to update session") return } if session == nil { respondError(w, http.StatusNotFound, "session not found") return } respondJSON(w, http.StatusOK, session) } func (h *SessionHandler) delete(w http.ResponseWriter, r *http.Request, id int64) { err := h.repo.Delete(r.Context(), id) if err == sql.ErrNoRows { respondError(w, http.StatusNotFound, "session not found") return } if err != nil { respondError(w, http.StatusInternalServerError, "failed to delete session") return } w.WriteHeader(http.StatusNoContent) } func (h *SessionHandler) addEntry(w http.ResponseWriter, r *http.Request, sessionID int64) { var req models.CreateSessionEntryRequest if err := decodeJSON(r, &req); err != nil { respondError(w, http.StatusBadRequest, "invalid request body") return } if req.ExerciseID == 0 { respondError(w, http.StatusBadRequest, "exercise_id is required") return } entry, err := h.repo.AddEntry(r.Context(), sessionID, &req) if err != nil { respondError(w, http.StatusInternalServerError, "failed to add session entry") return } respondJSON(w, http.StatusCreated, entry) }