package handlers import ( "fmt" "net/http" "time" apperrors "veza-backend-api/internal/errors" "veza-backend-api/internal/services" "veza-backend-api/internal/types" "github.com/gin-gonic/gin" "github.com/google/uuid" "go.uber.org/zap" ) // UserServiceInterfaceForSettings defines the interface for user service operations needed by settings handler // This allows for easier testing with mocks type UserServiceInterfaceForSettings interface { GetUserSettings(userID uuid.UUID) (*types.UserSettingsResponse, error) UpdateUserSettings(userID uuid.UUID, req *types.UpdateSettingsRequest) error } // SettingsHandler handles settings-related operations type SettingsHandler struct { userService UserServiceInterfaceForSettings commonHandler *CommonHandler } // NewSettingsHandler creates a new SettingsHandler instance func NewSettingsHandler(userService *services.UserService, logger *zap.Logger) *SettingsHandler { return &SettingsHandler{ userService: userService, commonHandler: NewCommonHandler(logger), } } // NewSettingsHandlerWithInterface creates a new SettingsHandler with an interface (for testing) func NewSettingsHandlerWithInterface(userService UserServiceInterfaceForSettings, logger *zap.Logger) *SettingsHandler { return &SettingsHandler{ userService: userService, commonHandler: NewCommonHandler(logger), } } // UserSettingsResponse represents the response structure for user settings type UserSettingsResponse struct { Notifications NotificationSettings `json:"notifications"` Privacy PrivacySettings `json:"privacy"` Content ContentSettings `json:"content"` Preferences PreferenceSettings `json:"preferences"` } // NotificationSettings represents notification preferences type NotificationSettings struct { EmailNotifications bool `json:"email_notifications"` PushNotifications bool `json:"push_notifications"` BrowserNotifications bool `json:"browser_notifications"` EmailOnFollow bool `json:"email_on_follow"` EmailOnLike bool `json:"email_on_like"` EmailOnComment bool `json:"email_on_comment"` EmailOnMessage bool `json:"email_on_message"` EmailOnMention bool `json:"email_on_mention"` EmailMarketing bool `json:"email_marketing"` } // PrivacySettings represents privacy preferences type PrivacySettings struct { AllowSearchIndexing bool `json:"allow_search_indexing"` ShowActivity bool `json:"show_activity"` } // ContentSettings represents content preferences type ContentSettings struct { ExplicitContent bool `json:"explicit_content"` Autoplay bool `json:"autoplay"` } // PreferenceSettings represents user preferences type PreferenceSettings struct { Language string `json:"language"` // ISO 639-1 Timezone string `json:"timezone"` Theme string `json:"theme"` // light, dark, auto } // GetSettings retrieves user settings // T0231: Utilise l'utilisateur authentifié depuis le contexte (route /users/settings sans :id) func (h *SettingsHandler) GetSettings(c *gin.Context) { // Récupérer l'ID utilisateur depuis le contexte d'authentification userID, ok := GetUserIDUUID(c) if !ok { return // Erreur déjà envoyée par GetUserIDUUID } if userID == uuid.Nil { RespondWithAppError(c, apperrors.NewUnauthorizedError("user not authenticated")) return } settings, err := h.userService.GetUserSettings(userID) if err != nil { RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to get settings", err)) return } RespondSuccess(c, http.StatusOK, settings) } // UpdateSettings updates user settings // T0232: Utilise l'utilisateur authentifié depuis le contexte (route /users/settings sans :id) func (h *SettingsHandler) UpdateSettings(c *gin.Context) { // Récupérer l'ID utilisateur depuis le contexte d'authentification userID, ok := GetUserIDUUID(c) if !ok { return // Erreur déjà envoyée par GetUserIDUUID } if userID == uuid.Nil { RespondWithAppError(c, apperrors.NewUnauthorizedError("user not authenticated")) return } var req types.UpdateSettingsRequest if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil { RespondWithAppError(c, appErr) return } // Valider preferences si fournies if req.Preferences != nil { if err := h.validatePreferences(req.Preferences); err != nil { RespondWithAppError(c, apperrors.NewValidationError(err.Error())) return } } // Mettre à jour settings if err := h.userService.UpdateUserSettings(userID, &req); err != nil { RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to update settings", err)) return } RespondSuccess(c, http.StatusOK, gin.H{"message": "settings updated"}) } // validatePreferences validates preference settings func (h *SettingsHandler) validatePreferences(prefs *types.PreferenceSettings) error { // Valider language (ISO 639-1) validLanguages := []string{"en", "fr", "es", "de", "it", "pt", "ru", "ja", "zh", "ko"} if prefs.Language != "" { valid := false for _, lang := range validLanguages { if prefs.Language == lang { valid = true break } } if !valid { return fmt.Errorf("invalid language code: %s", prefs.Language) } } // Valider timezone (IANA timezone) if prefs.Timezone != "" { if _, err := time.LoadLocation(prefs.Timezone); err != nil { return fmt.Errorf("invalid timezone: %s", prefs.Timezone) } } return nil }