package services import ( "context" "fmt" "github.com/google/uuid" "veza-backend-api/internal/models" "go.uber.org/zap" "gorm.io/gorm" ) // TrackLikeService gère les opérations sur les likes de tracks type TrackLikeService struct { db *gorm.DB logger *zap.Logger } // NewTrackLikeService crée un nouveau service de likes de tracks func NewTrackLikeService(db *gorm.DB, logger *zap.Logger) *TrackLikeService { if logger == nil { logger = zap.NewNop() } return &TrackLikeService{ db: db, logger: logger, } } // LikeTrack ajoute un like d'un utilisateur sur un track // MIGRATION UUID: userID migré vers uuid.UUID, trackID reste int64 - Corrected: trackID est maintenant uuid.UUID func (s *TrackLikeService) LikeTrack(ctx context.Context, userID uuid.UUID, trackID uuid.UUID) error { // Changed trackID to uuid.UUID // Vérifier si le track existe var track models.Track if err := s.db.WithContext(ctx).First(&track, "id = ?", trackID).Error; err != nil { // Updated query if err == gorm.ErrRecordNotFound { return fmt.Errorf("track not found") } return fmt.Errorf("failed to check track: %w", err) } // Vérifier si l'utilisateur a déjà liké ce track var existing models.TrackLike if err := s.db.WithContext(ctx).Where("user_id = ? AND track_id = ?", userID, trackID).First(&existing).Error; err == nil { // Déjà liké, retourner nil (idempotent) return nil } else if err != gorm.ErrRecordNotFound { return fmt.Errorf("failed to check existing like: %w", err) } // Créer le like like := models.TrackLike{ UserID: userID, TrackID: trackID, } if err := s.db.WithContext(ctx).Create(&like).Error; err != nil { return fmt.Errorf("failed to create like: %w", err) } // Mettre à jour le compteur de likes du track if err := s.db.WithContext(ctx).Model(&track).UpdateColumn("like_count", gorm.Expr("like_count + ?", 1)).Error; err != nil { s.logger.Warn("Failed to update track like_count", zap.Any("track_id", trackID), // Changed to zap.Any for uuid.UUID zap.Error(err), ) // Ne pas retourner l'erreur, le like a été créé avec succès } s.logger.Info("Track liked", zap.String("user_id", userID.String()), zap.Any("track_id", trackID), // Changed to zap.Any for uuid.UUID ) return nil } // UnlikeTrack supprime un like d'un utilisateur sur un track // MIGRATION UUID: userID migré vers uuid.UUID, trackID reste int64 - Corrected: trackID est maintenant uuid.UUID func (s *TrackLikeService) UnlikeTrack(ctx context.Context, userID uuid.UUID, trackID uuid.UUID) error { // Changed trackID to uuid.UUID // Vérifier si le like existe var like models.TrackLike if err := s.db.WithContext(ctx).Where("user_id = ? AND track_id = ?", userID, trackID).First(&like).Error; err != nil { if err == gorm.ErrRecordNotFound { // Pas de like à supprimer, retourner nil (idempotent) return nil } return fmt.Errorf("failed to check like: %w", err) } // Supprimer le like if err := s.db.WithContext(ctx).Delete(&like).Error; err != nil { return fmt.Errorf("failed to delete like: %w", err) } // Mettre à jour le compteur de likes du track var track models.Track if err := s.db.WithContext(ctx).First(&track, "id = ?", trackID).Error; err == nil { // Updated query // Use CASE expression for SQLite compatibility (GREATEST is not supported in SQLite) if err := s.db.WithContext(ctx).Model(&track).UpdateColumn("like_count", gorm.Expr("CASE WHEN like_count - 1 < 0 THEN 0 ELSE like_count - 1 END")).Error; err != nil { s.logger.Warn("Failed to update track like_count", zap.Any("track_id", trackID), // Changed to zap.Any for uuid.UUID zap.Error(err), ) // Ne pas retourner l'erreur, le like a été supprimé avec succès } } s.logger.Info("Track unliked", zap.String("user_id", userID.String()), zap.Any("track_id", trackID), // Changed to zap.Any for uuid.UUID ) return nil } // IsLiked vérifie si un utilisateur a liké un track func (s *TrackLikeService) IsLiked(ctx context.Context, userID uuid.UUID, trackID uuid.UUID) (bool, error) { // Changed trackID to uuid.UUID var count int64 err := s.db.WithContext(ctx).Model(&models.TrackLike{}). Where("user_id = ? AND track_id = ?", userID, trackID). Count(&count).Error if err != nil { return false, fmt.Errorf("failed to check like: %w", err) } return count > 0, nil } // GetTrackLikesCount retourne le nombre de likes d'un track func (s *TrackLikeService) GetTrackLikesCount(ctx context.Context, trackID uuid.UUID) (int64, error) { // Changed trackID to uuid.UUID var count int64 err := s.db.WithContext(ctx).Model(&models.TrackLike{}). Where("track_id = ?", trackID). Count(&count).Error if err != nil { return 0, fmt.Errorf("failed to get likes count: %w", err) } return count, nil } // GetUserLikedTracks retourne la liste des tracks likés par un utilisateur func (s *TrackLikeService) GetUserLikedTracks(ctx context.Context, userID uuid.UUID, limit, offset int) ([]models.Track, error) { var tracks []models.Track query := s.db.WithContext(ctx). Joins("INNER JOIN track_likes ON tracks.id = track_likes.track_id"). Where("track_likes.user_id = ?", userID). Order("track_likes.created_at DESC") if limit > 0 { query = query.Limit(limit) } if offset > 0 { query = query.Offset(offset) } if err := query.Find(&tracks).Error; err != nil { return nil, fmt.Errorf("failed to get user liked tracks: %w", err) } return tracks, nil } // GetUserLikedTracksCount retourne le nombre total de tracks likés par un utilisateur func (s *TrackLikeService) GetUserLikedTracksCount(ctx context.Context, userID uuid.UUID) (int64, error) { var count int64 err := s.db.WithContext(ctx).Model(&models.TrackLike{}). Where("user_id = ?", userID). Count(&count).Error if err != nil { return 0, fmt.Errorf("failed to get user liked tracks count: %w", err) } return count, nil }