package handlers import ( "net/http" "strings" "time" "github.com/calendarapi/internal/middleware" "github.com/calendarapi/internal/models" "github.com/calendarapi/internal/service" "github.com/calendarapi/internal/utils" "github.com/go-chi/chi/v5" ) type AvailabilityHandler struct { availSvc *service.AvailabilityService } func NewAvailabilityHandler(availSvc *service.AvailabilityService) *AvailabilityHandler { return &AvailabilityHandler{availSvc: availSvc} } // GetByToken returns busy blocks for a calendar by its public token. No auth required. func (h *AvailabilityHandler) GetByToken(w http.ResponseWriter, r *http.Request) { token := chi.URLParam(r, "token") if token == "" { utils.WriteError(w, models.ErrNotFound) return } startStr := r.URL.Query().Get("start") endStr := r.URL.Query().Get("end") if startStr == "" || endStr == "" { utils.WriteError(w, models.NewValidationError("start and end required")) return } start, err := time.Parse(time.RFC3339, startStr) if err != nil { utils.WriteError(w, models.NewValidationError("invalid start time")) return } end, err := time.Parse(time.RFC3339, endStr) if err != nil { utils.WriteError(w, models.NewValidationError("invalid end time")) return } result, err := h.availSvc.GetBusyBlocksByTokenPublic(r.Context(), token, start, end) if err != nil { utils.WriteError(w, err) return } utils.WriteJSON(w, http.StatusOK, result) } // GetAggregate returns merged busy blocks across multiple calendars by their tokens. No auth required. func (h *AvailabilityHandler) GetAggregate(w http.ResponseWriter, r *http.Request) { tokensStr := r.URL.Query().Get("tokens") if tokensStr == "" { utils.WriteError(w, models.NewValidationError("tokens required (comma-separated)")) return } tokens := strings.Split(tokensStr, ",") startStr := r.URL.Query().Get("start") endStr := r.URL.Query().Get("end") if startStr == "" || endStr == "" { utils.WriteError(w, models.NewValidationError("start and end required")) return } start, err := time.Parse(time.RFC3339, startStr) if err != nil { utils.WriteError(w, models.NewValidationError("invalid start time")) return } end, err := time.Parse(time.RFC3339, endStr) if err != nil { utils.WriteError(w, models.NewValidationError("invalid end time")) return } result, err := h.availSvc.GetBusyBlocksAggregate(r.Context(), tokens, start, end) if err != nil { utils.WriteError(w, err) return } utils.WriteJSON(w, http.StatusOK, result) } func (h *AvailabilityHandler) Get(w http.ResponseWriter, r *http.Request) { userID, _ := middleware.GetUserID(r.Context()) q := r.URL.Query() calIDStr := q.Get("calendar_id") if calIDStr == "" { utils.WriteError(w, models.NewValidationError("calendar_id required")) return } calID, err := utils.ValidateUUID(calIDStr) if err != nil { utils.WriteError(w, err) return } startStr := q.Get("start") endStr := q.Get("end") if startStr == "" || endStr == "" { utils.WriteError(w, models.NewValidationError("start and end required")) return } start, err := time.Parse(time.RFC3339, startStr) if err != nil { utils.WriteError(w, models.NewValidationError("invalid start time")) return } end, err := time.Parse(time.RFC3339, endStr) if err != nil { utils.WriteError(w, models.NewValidationError("invalid end time")) return } result, err := h.availSvc.GetBusyBlocks(r.Context(), userID, calID, start, end) if err != nil { utils.WriteError(w, err) return } utils.WriteJSON(w, http.StatusOK, result) }