package handler import ( "encoding/json" "errors" "net/http" "strconv" "github.com/go-chi/chi/v5" "financeiro-carvalho/internal/model" "financeiro-carvalho/internal/repository" "financeiro-carvalho/internal/service" ) type RecurringHandler struct { svc *service.RecurringService } func NewRecurringHandler(svc *service.RecurringService) *RecurringHandler { return &RecurringHandler{svc: svc} } func (h *RecurringHandler) List(w http.ResponseWriter, r *http.Request) { items, err := h.svc.List(r.Context()) if err != nil { respondError(w, http.StatusInternalServerError, "failed to list recurring expenses") return } respondJSON(w, http.StatusOK, items) } func (h *RecurringHandler) Create(w http.ResponseWriter, r *http.Request) { var in model.RecurringInput if err := json.NewDecoder(r.Body).Decode(&in); err != nil { respondError(w, http.StatusBadRequest, "invalid JSON") return } out, err := h.svc.Create(r.Context(), in) if err != nil { respondError(w, http.StatusBadRequest, err.Error()) return } respondJSON(w, http.StatusCreated, out) } func (h *RecurringHandler) Update(w http.ResponseWriter, r *http.Request) { id, err := strconv.Atoi(chi.URLParam(r, "id")) if err != nil { respondError(w, http.StatusBadRequest, "invalid id") return } var in model.RecurringInput if err := json.NewDecoder(r.Body).Decode(&in); err != nil { respondError(w, http.StatusBadRequest, "invalid JSON") return } out, err := h.svc.Update(r.Context(), id, in) if errors.Is(err, repository.ErrNotFound) { respondError(w, http.StatusNotFound, "not found") return } if err != nil { respondError(w, http.StatusBadRequest, err.Error()) return } respondJSON(w, http.StatusOK, out) } func (h *RecurringHandler) Delete(w http.ResponseWriter, r *http.Request) { id, err := strconv.Atoi(chi.URLParam(r, "id")) if err != nil { respondError(w, http.StatusBadRequest, "invalid id") return } if err := h.svc.Delete(r.Context(), id); err != nil { respondError(w, http.StatusNotFound, "not found") return } w.WriteHeader(http.StatusNoContent) } func (h *RecurringHandler) MonthlyStatus(w http.ResponseWriter, r *http.Request) { month := r.URL.Query().Get("month") if month == "" { respondError(w, http.StatusBadRequest, "month param required (YYYY-MM)") return } statuses, err := h.svc.MonthlyStatus(r.Context(), month) if err != nil { respondError(w, http.StatusInternalServerError, "failed to compute status") return } respondJSON(w, http.StatusOK, statuses) } func (h *RecurringHandler) Ignore(w http.ResponseWriter, r *http.Request) { id, err := strconv.Atoi(chi.URLParam(r, "id")) if err != nil { respondError(w, http.StatusBadRequest, "invalid id") return } var body struct { Month string `json:"month"` Reason string `json:"reason"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { respondError(w, http.StatusBadRequest, "invalid JSON") return } if err := h.svc.Ignore(r.Context(), id, body.Month, body.Reason); err != nil { respondError(w, http.StatusInternalServerError, "failed to ignore") return } w.WriteHeader(http.StatusNoContent) } func (h *RecurringHandler) Unignore(w http.ResponseWriter, r *http.Request) { id, err := strconv.Atoi(chi.URLParam(r, "id")) if err != nil { respondError(w, http.StatusBadRequest, "invalid id") return } month := r.URL.Query().Get("month") if err := h.svc.Unignore(r.Context(), id, month); err != nil { respondError(w, http.StatusInternalServerError, "failed to unignore") return } w.WriteHeader(http.StatusNoContent) }