package handlers import ( "net/http" "strconv" "github.com/labstack/echo/v4" "github.com/treytartt/casera-api/internal/apperrors" "github.com/treytartt/casera-api/internal/dto/requests" "github.com/treytartt/casera-api/internal/i18n" "github.com/treytartt/casera-api/internal/middleware" "github.com/treytartt/casera-api/internal/services" "github.com/treytartt/casera-api/internal/validator" ) // ResidenceHandler handles residence-related HTTP requests type ResidenceHandler struct { residenceService *services.ResidenceService pdfService *services.PDFService emailService *services.EmailService pdfReportsEnabled bool } // NewResidenceHandler creates a new residence handler func NewResidenceHandler(residenceService *services.ResidenceService, pdfService *services.PDFService, emailService *services.EmailService, pdfReportsEnabled bool) *ResidenceHandler { return &ResidenceHandler{ residenceService: residenceService, pdfService: pdfService, emailService: emailService, pdfReportsEnabled: pdfReportsEnabled, } } // ListResidences handles GET /api/residences/ func (h *ResidenceHandler) ListResidences(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } response, err := h.residenceService.ListResidences(user.ID) if err != nil { return err } return c.JSON(http.StatusOK, response) } // GetMyResidences handles GET /api/residences/my-residences/ func (h *ResidenceHandler) GetMyResidences(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } userNow := middleware.GetUserNow(c) response, err := h.residenceService.GetMyResidences(user.ID, userNow) if err != nil { return err } return c.JSON(http.StatusOK, response) } // GetSummary handles GET /api/residences/summary/ // Returns just the task statistics summary without full residence data func (h *ResidenceHandler) GetSummary(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } userNow := middleware.GetUserNow(c) summary, err := h.residenceService.GetSummary(user.ID, userNow) if err != nil { return err } return c.JSON(http.StatusOK, summary) } // GetResidence handles GET /api/residences/:id/ func (h *ResidenceHandler) GetResidence(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return apperrors.BadRequest("error.invalid_residence_id") } response, err := h.residenceService.GetResidence(uint(residenceID), user.ID) if err != nil { return err } return c.JSON(http.StatusOK, response) } // CreateResidence handles POST /api/residences/ func (h *ResidenceHandler) CreateResidence(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } var req requests.CreateResidenceRequest if err := c.Bind(&req); err != nil { return apperrors.BadRequest("error.invalid_request") } if err := c.Validate(&req); err != nil { return c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err)) } response, err := h.residenceService.CreateResidence(&req, user.ID) if err != nil { return err } return c.JSON(http.StatusCreated, response) } // UpdateResidence handles PUT/PATCH /api/residences/:id/ func (h *ResidenceHandler) UpdateResidence(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return apperrors.BadRequest("error.invalid_residence_id") } var req requests.UpdateResidenceRequest if err := c.Bind(&req); err != nil { return apperrors.BadRequest("error.invalid_request") } if err := c.Validate(&req); err != nil { return c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err)) } response, err := h.residenceService.UpdateResidence(uint(residenceID), user.ID, &req) if err != nil { return err } return c.JSON(http.StatusOK, response) } // DeleteResidence handles DELETE /api/residences/:id/ func (h *ResidenceHandler) DeleteResidence(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return apperrors.BadRequest("error.invalid_residence_id") } response, err := h.residenceService.DeleteResidence(uint(residenceID), user.ID) if err != nil { return err } return c.JSON(http.StatusOK, response) } // GetShareCode handles GET /api/residences/:id/share-code/ // Returns the active share code for a residence, or null if none exists func (h *ResidenceHandler) GetShareCode(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return apperrors.BadRequest("error.invalid_residence_id") } shareCode, err := h.residenceService.GetShareCode(uint(residenceID), user.ID) if err != nil { return err } if shareCode == nil { return c.JSON(http.StatusOK, map[string]interface{}{"share_code": nil}) } return c.JSON(http.StatusOK, map[string]interface{}{"share_code": shareCode}) } // GenerateShareCode handles POST /api/residences/:id/generate-share-code/ func (h *ResidenceHandler) GenerateShareCode(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return apperrors.BadRequest("error.invalid_residence_id") } var req requests.GenerateShareCodeRequest // Request body is optional c.Bind(&req) response, err := h.residenceService.GenerateShareCode(uint(residenceID), user.ID, req.ExpiresInHours) if err != nil { return err } return c.JSON(http.StatusOK, response) } // GenerateSharePackage handles POST /api/residences/:id/generate-share-package/ // Returns a share code with metadata for creating a .casera package file func (h *ResidenceHandler) GenerateSharePackage(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return apperrors.BadRequest("error.invalid_residence_id") } var req requests.GenerateShareCodeRequest // Request body is optional (for expires_in_hours) c.Bind(&req) response, err := h.residenceService.GenerateSharePackage(uint(residenceID), user.ID, req.ExpiresInHours) if err != nil { return err } return c.JSON(http.StatusOK, response) } // JoinWithCode handles POST /api/residences/join-with-code/ func (h *ResidenceHandler) JoinWithCode(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } var req requests.JoinWithCodeRequest if err := c.Bind(&req); err != nil { return apperrors.BadRequest("error.invalid_request") } if err := c.Validate(&req); err != nil { return err } response, err := h.residenceService.JoinWithCode(req.Code, user.ID) if err != nil { return err } return c.JSON(http.StatusOK, response) } // GetResidenceUsers handles GET /api/residences/:id/users/ func (h *ResidenceHandler) GetResidenceUsers(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return apperrors.BadRequest("error.invalid_residence_id") } users, err := h.residenceService.GetResidenceUsers(uint(residenceID), user.ID) if err != nil { return err } return c.JSON(http.StatusOK, users) } // RemoveResidenceUser handles DELETE /api/residences/:id/users/:user_id/ func (h *ResidenceHandler) RemoveResidenceUser(c echo.Context) error { user, err := middleware.MustGetAuthUser(c) if err != nil { return err } residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return apperrors.BadRequest("error.invalid_residence_id") } userIDToRemove, err := strconv.ParseUint(c.Param("user_id"), 10, 32) if err != nil { return apperrors.BadRequest("error.invalid_user_id") } err = h.residenceService.RemoveUser(uint(residenceID), uint(userIDToRemove), user.ID) if err != nil { return err } return c.JSON(http.StatusOK, map[string]interface{}{"message": i18n.LocalizedMessage(c, "message.user_removed")}) } // GetResidenceTypes handles GET /api/residences/types/ func (h *ResidenceHandler) GetResidenceTypes(c echo.Context) error { types, err := h.residenceService.GetResidenceTypes() if err != nil { return err } return c.JSON(http.StatusOK, types) } // GenerateTasksReport handles POST /api/residences/:id/generate-tasks-report/ // Generates a PDF report of tasks for the residence and emails it func (h *ResidenceHandler) GenerateTasksReport(c echo.Context) error { if !h.pdfReportsEnabled { return apperrors.BadRequest("error.feature_disabled") } user, err := middleware.MustGetAuthUser(c) if err != nil { return err } residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return apperrors.BadRequest("error.invalid_residence_id") } // Optional request body for email recipient var req struct { Email string `json:"email"` } c.Bind(&req) // Generate the report data report, err := h.residenceService.GenerateTasksReport(uint(residenceID), user.ID) if err != nil { return err } // Determine recipient email recipientEmail := req.Email if recipientEmail == "" { recipientEmail = user.Email } // Get recipient name recipientName := user.FirstName if recipientName == "" { recipientName = user.Username } // Generate PDF if PDF service is available var pdfGenerated bool var emailSent bool if h.pdfService != nil && h.emailService != nil { pdfData, pdfErr := h.pdfService.GenerateTasksReportPDF(report) if pdfErr == nil { pdfGenerated = true // Send email with PDF attachment emailErr := h.emailService.SendTasksReportEmail( recipientEmail, recipientName, report.ResidenceName, report.TotalTasks, report.Completed, report.Pending, report.Overdue, pdfData, ) if emailErr == nil { emailSent = true } } } // Build response message message := i18n.LocalizedMessage(c, "message.tasks_report_generated") if pdfGenerated && emailSent { message = i18n.LocalizedMessageWithData(c, "message.tasks_report_sent", map[string]interface{}{"Email": recipientEmail}) } else if pdfGenerated && !emailSent { message = i18n.LocalizedMessage(c, "message.tasks_report_email_failed") } return c.JSON(http.StatusOK, map[string]interface{}{ "message": message, "residence_name": report.ResidenceName, "recipient_email": recipientEmail, "pdf_generated": pdfGenerated, "email_sent": emailSent, "report": report, }) }