package api import ( "encoding/json" "errors" "net/http" "github.com/alchemistkay/guestguard/internal/domain" "github.com/alchemistkay/guestguard/internal/storage" ) type guestHandler struct { guests *storage.GuestRepo events *storage.EventRepo } type createGuestRequest struct { Name string `json:"name"` Email *string `json:"email"` Phone *string `json:"phone"` PlusOnes int `json:"plus_ones"` DietaryNotes *string `json:"dietary_notes"` TableNumber *int `json:"table_number"` } func (h *guestHandler) create(w http.ResponseWriter, r *http.Request) { eventID, ok := parseIDParam(w, r, "id") if !ok { return } if _, err := h.events.Get(r.Context(), eventID); err != nil { if errors.Is(err, domain.ErrEventNotFound) { writeError(w, http.StatusNotFound, "event not found") return } writeError(w, http.StatusInternalServerError, "failed to load event") return } var req createGuestRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid json") return } if req.Name == "" { writeError(w, http.StatusBadRequest, "name is required") return } if req.PlusOnes < 0 { writeError(w, http.StatusBadRequest, "plus_ones must be >= 0") return } g, err := h.guests.Create(r.Context(), storage.CreateGuestParams{ EventID: eventID, Name: req.Name, Email: req.Email, Phone: req.Phone, PlusOnes: req.PlusOnes, DietaryNotes: req.DietaryNotes, TableNumber: req.TableNumber, }) if err != nil { writeError(w, http.StatusInternalServerError, "failed to create guest") return } writeJSON(w, http.StatusCreated, g) } func (h *guestHandler) list(w http.ResponseWriter, r *http.Request) { eventID, ok := parseIDParam(w, r, "id") if !ok { return } q := r.URL.Query() limit := atoiOr(q.Get("limit"), 100) offset := atoiOr(q.Get("offset"), 0) guests, err := h.guests.ListByEventWithRSVP(r.Context(), eventID, limit, offset) if err != nil { writeError(w, http.StatusInternalServerError, "failed to list guests") return } if guests == nil { guests = []*storage.GuestWithRSVP{} } stats := struct { Total int `json:"total"` Attending int `json:"attending"` Declined int `json:"declined"` Maybe int `json:"maybe"` Pending int `json:"pending"` }{Total: len(guests)} for _, g := range guests { switch { case g.RSVPResponse == nil: stats.Pending++ case *g.RSVPResponse == string(domain.RSVPAttending): stats.Attending++ case *g.RSVPResponse == string(domain.RSVPDeclined): stats.Declined++ case *g.RSVPResponse == string(domain.RSVPMaybe): stats.Maybe++ } } writeJSON(w, http.StatusOK, map[string]any{ "guests": guests, "stats": stats, "limit": limit, "offset": offset, }) }