package diary import ( "context" "encoding/json" "log/slog" "net/http" "github.com/food-ai/backend/internal/infra/middleware" "github.com/go-chi/chi/v5" ) // DiaryRepository is the data layer interface used by Handler. type DiaryRepository interface { ListByDate(ctx context.Context, userID, date string) ([]*Entry, error) Create(ctx context.Context, userID string, req CreateRequest) (*Entry, error) Delete(ctx context.Context, id, userID string) error } // Handler handles diary endpoints. type Handler struct { repo DiaryRepository } // NewHandler creates a new Handler. func NewHandler(repo DiaryRepository) *Handler { return &Handler{repo: repo} } // GetByDate handles GET /diary?date=YYYY-MM-DD func (h *Handler) GetByDate(w http.ResponseWriter, r *http.Request) { userID := middleware.UserIDFromCtx(r.Context()) if userID == "" { writeError(w, http.StatusUnauthorized, "unauthorized") return } date := r.URL.Query().Get("date") if date == "" { writeError(w, http.StatusBadRequest, "date query parameter required (YYYY-MM-DD)") return } entries, err := h.repo.ListByDate(r.Context(), userID, date) if err != nil { slog.Error("list diary by date", "err", err) writeError(w, http.StatusInternalServerError, "failed to load diary") return } if entries == nil { entries = []*Entry{} } writeJSON(w, http.StatusOK, entries) } // Create handles POST /diary func (h *Handler) Create(w http.ResponseWriter, r *http.Request) { userID := middleware.UserIDFromCtx(r.Context()) if userID == "" { writeError(w, http.StatusUnauthorized, "unauthorized") return } var req CreateRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } if req.Date == "" || req.Name == "" || req.MealType == "" { writeError(w, http.StatusBadRequest, "date, meal_type and name are required") return } entry, err := h.repo.Create(r.Context(), userID, req) if err != nil { slog.Error("create diary entry", "err", err) writeError(w, http.StatusInternalServerError, "failed to create diary entry") return } writeJSON(w, http.StatusCreated, entry) } // Delete handles DELETE /diary/{id} func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { userID := middleware.UserIDFromCtx(r.Context()) if userID == "" { writeError(w, http.StatusUnauthorized, "unauthorized") return } id := chi.URLParam(r, "id") if err := h.repo.Delete(r.Context(), id, userID); err != nil { if err == ErrNotFound { writeError(w, http.StatusNotFound, "diary entry not found") return } slog.Error("delete diary entry", "err", err) writeError(w, http.StatusInternalServerError, "failed to delete diary entry") return } w.WriteHeader(http.StatusNoContent) } type errorResponse struct { Error string `json:"error"` } func writeError(w http.ResponseWriter, status int, msg string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(errorResponse{Error: msg}) } func writeJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(v) }