package controllers import ( "net/http" "strings" "fotbal-club/internal/models" "fotbal-club/pkg/email" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // NotificationsController handles admin-triggered notifications (emails) // It reuses the newsletter sending pipeline/templates. type NotificationsController struct { DB *gorm.DB EmailService email.EmailService } func NewNotificationsController(db *gorm.DB, emailService email.EmailService) *NotificationsController { return &NotificationsController{DB: db, EmailService: emailService} } // Common request payload for notifications // Subject is required; body/content carries HTML. Recipients optional. // If SendToSubscribers is true, all active newsletter subscribers will be included. // Optional context identifiers (competition code or match ext ID) are accepted for logging/auditing // or future template specialization, but are not required for sending right now. type NotificationRequest struct { Subject string `json:"subject" binding:"required"` Body string `json:"body"` Content string `json:"content"` Recipients []string `json:"recipients"` SendToSubscribers bool `json:"send_to_subscribers"` CompetitionCode string `json:"competition_code"` MatchExternalID string `json:"match_external_id"` } // SendCompetitionNotification sends a notification related to a competition // @Summary Send competition notification (admin) // @Description Sends a notification email for a competition to selected recipients and/or newsletter subscribers // @Tags admin // @Security Bearer // @Accept json // @Produce json // @Param input body NotificationRequest true "Notification payload" // @Success 200 {object} map[string]any // @Failure 400 {object} map[string]string // @Failure 401 {object} map[string]string // @Failure 403 {object} map[string]string // @Router /api/v1/admin/notifications/competition [post] func (nc *NotificationsController) SendCompetitionNotification(c *gin.Context) { nc.sendNotification(c) } // SendMatchNotification sends a notification related to a match // @Summary Send match notification (admin) // @Description Sends a notification email for a match to selected recipients and/or newsletter subscribers // @Tags admin // @Security Bearer // @Accept json // @Produce json // @Param input body NotificationRequest true "Notification payload" // @Success 200 {object} map[string]any // @Failure 400 {object} map[string]string // @Failure 401 {object} map[string]string // @Failure 403 {object} map[string]string // @Router /api/v1/admin/notifications/match [post] func (nc *NotificationsController) SendMatchNotification(c *gin.Context) { nc.sendNotification(c) } // Internal shared implementation func (nc *NotificationsController) sendNotification(c *gin.Context) { // Only admins if c.GetString("userRole") != "admin" { c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"}) return } var input NotificationRequest if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"}) return } // Prefer Body then Content body := strings.TrimSpace(input.Body) if body == "" { body = strings.TrimSpace(input.Content) } if input.Subject == "" || body == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "subject and body/content are required"}) return } // Build recipients recipients := make([]string, 0, len(input.Recipients)+16) // Explicit recipients for _, r := range input.Recipients { r = strings.TrimSpace(strings.ToLower(r)) if r != "" { recipients = append(recipients, r) } } // Include subscribers if requested if input.SendToSubscribers { var subs []models.NewsletterSubscription if err := nc.DB.Where("is_active = ?", true).Find(&subs).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load subscribers"}) return } for _, s := range subs { if s.Email != "" { recipients = append(recipients, strings.ToLower(strings.TrimSpace(s.Email))) } } } // Dedupe recipients uniq := make(map[string]struct{}, len(recipients)) out := make([]string, 0, len(recipients)) for _, r := range recipients { if r == "" { continue } if _, exists := uniq[r]; !exists { uniq[r] = struct{}{} out = append(out, r) } } if len(out) == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "No recipients resolved"}) return } // Send using newsletter template/pipeline data := &email.NewsletterData{ Subject: input.Subject, Content: body, Recipients: out, } if err := nc.EmailService.SendNewsletter(data); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to send notifications"}) return } c.JSON(http.StatusOK, gin.H{ "message": "Notifications sent", "recipients": len(out), "competition": input.CompetitionCode, "match": input.MatchExternalID, }) }