package handlers import ( "net/http" "time" "github.com/breakpilot/school-service/internal/models" "github.com/gin-gonic/gin" ) // ListCalendarHolidays returns OpenHolidaysAPI events for a region + range. // Query params: ?region=DE-NI&from=2026-08-01&to=2027-07-31. If omitted, // region falls back to the caller's saved config and the range to the // current calendar year. func (h *Handler) ListCalendarHolidays(c *gin.Context) { uid := getUserID(c) if uid == "" { respondError(c, http.StatusUnauthorized, "User not authenticated") return } region := c.Query("region") if region == "" { cfg, err := h.calendarService.GetConfig(c.Request.Context(), uid) if err != nil || cfg == nil { respondError(c, http.StatusBadRequest, "region query param required (no saved config)") return } region = cfg.Bundesland } from := c.DefaultQuery("from", time.Now().Format("2006-01-02")) to := c.DefaultQuery("to", time.Now().AddDate(1, 0, 0).Format("2006-01-02")) events, err := h.calendarService.ListHolidays(c.Request.Context(), region, from, to) if err != nil { respondError(c, http.StatusInternalServerError, "Failed to load holidays: "+err.Error()) return } if events == nil { events = []models.PublicEvent{} } respondSuccess(c, events) } func (h *Handler) GetCalendarConfig(c *gin.Context) { uid := getUserID(c) if uid == "" { respondError(c, http.StatusUnauthorized, "User not authenticated") return } cfg, err := h.calendarService.GetConfig(c.Request.Context(), uid) if err != nil { // No row → 200 with null so the wizard knows to prompt. respondSuccess(c, nil) return } respondSuccess(c, cfg) } func (h *Handler) UpsertCalendarConfig(c *gin.Context) { uid := getUserID(c) if uid == "" { respondError(c, http.StatusUnauthorized, "User not authenticated") return } var req models.UpsertSchoolCalendarConfigRequest if err := c.ShouldBindJSON(&req); err != nil { respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error()) return } cfg, err := h.calendarService.UpsertConfig(c.Request.Context(), uid, &req) if err != nil { respondError(c, http.StatusInternalServerError, "Failed to save config: "+err.Error()) return } respondCreated(c, cfg) } // ---------- School Events (Phase 9b) ---------- func (h *Handler) CreateSchoolEvent(c *gin.Context) { uid := getUserID(c) if uid == "" { respondError(c, http.StatusUnauthorized, "User not authenticated") return } var req models.CreateSchoolEventRequest if err := c.ShouldBindJSON(&req); err != nil { respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error()) return } ev, err := h.calendarService.CreateEvent(c.Request.Context(), uid, &req) if err != nil { respondError(c, http.StatusInternalServerError, "Failed to create event: "+err.Error()) return } respondCreated(c, ev) } func (h *Handler) ListSchoolEvents(c *gin.Context) { uid := getUserID(c) if uid == "" { respondError(c, http.StatusUnauthorized, "User not authenticated") return } events, err := h.calendarService.ListEvents(c.Request.Context(), uid, c.Query("from"), c.Query("to")) if err != nil { respondError(c, http.StatusInternalServerError, "Failed to list events: "+err.Error()) return } if events == nil { events = []models.SchoolEvent{} } respondSuccess(c, events) } func (h *Handler) DeleteSchoolEvent(c *gin.Context) { uid := getUserID(c) if uid == "" { respondError(c, http.StatusUnauthorized, "User not authenticated") return } if err := h.calendarService.DeleteEvent(c.Request.Context(), c.Param("id"), uid); err != nil { respondError(c, http.StatusInternalServerError, "Failed to delete event: "+err.Error()) return } c.JSON(http.StatusOK, gin.H{"message": "Event deleted"}) } func (h *Handler) RolloverSchoolYear(c *gin.Context) { uid := getUserID(c) if uid == "" { respondError(c, http.StatusUnauthorized, "User not authenticated") return } var req models.SchoolYearRolloverRequest // Body is optional — empty defaults to next-Aug rollover. _ = c.ShouldBindJSON(&req) result, err := h.calendarService.RolloverSchoolYear(c.Request.Context(), uid, &req) if err != nil { respondError(c, http.StatusInternalServerError, "Rollover failed: "+err.Error()) return } respondSuccess(c, result) }