package services

import (
	"context"
	"errors"
	"fmt"

	"github.com/google/uuid"

	"veza-backend-api/internal/models"

	"go.uber.org/zap"
	"gorm.io/gorm"
)

// PlaylistFollowService gère les opérations sur les follows de playlists
// T0489: Create Playlist Follow Feature
type PlaylistFollowService struct {
	db     *gorm.DB
	logger *zap.Logger
}

// NewPlaylistFollowService crée un nouveau service de follows de playlists
func NewPlaylistFollowService(db *gorm.DB, logger *zap.Logger) *PlaylistFollowService {
	if logger == nil {
		logger = zap.NewNop()
	}
	return &PlaylistFollowService{
		db:     db,
		logger: logger,
	}
}

// FollowPlaylist ajoute un follow d'un utilisateur sur une playlist
// MIGRATION UUID: Completée. userID et playlistID sont des UUIDs.
func (s *PlaylistFollowService) FollowPlaylist(ctx context.Context, userID uuid.UUID, playlistID uuid.UUID) error {
	// Vérifier si la playlist existe
	var playlist models.Playlist
	if err := s.db.WithContext(ctx).First(&playlist, "id = ?", playlistID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.New("playlist not found")
		}
		return fmt.Errorf("failed to check playlist: %w", err)
	}

	// Vérifier si l'utilisateur est le propriétaire (ne peut pas suivre sa propre playlist)
	if playlist.UserID == userID {
		return errors.New("cannot follow own playlist")
	}

	// Vérifier si l'utilisateur suit déjà cette playlist
	var existing models.PlaylistFollow
	if err := s.db.WithContext(ctx).Where("user_id = ? AND playlist_id = ? AND deleted_at IS NULL", userID, playlistID).First(&existing).Error; err == nil {
		// Déjà suivi, retourner nil (idempotent)
		return nil
	} else if !errors.Is(err, gorm.ErrRecordNotFound) {
		return fmt.Errorf("failed to check existing follow: %w", err)
	}

	// Créer le follow
	follow := models.PlaylistFollow{
		UserID:     userID,
		PlaylistID: playlistID,
	}
	if err := s.db.WithContext(ctx).Create(&follow).Error; err != nil {
		return fmt.Errorf("failed to create follow: %w", err)
	}

	// Mettre à jour le compteur de followers de la playlist
	if err := s.db.WithContext(ctx).Model(&playlist).UpdateColumn("follower_count", gorm.Expr("follower_count + ?", 1)).Error; err != nil {
		s.logger.Warn("Failed to update playlist follower_count",
			zap.String("playlist_id", playlistID.String()),
			zap.Error(err),
		)
		// Ne pas retourner l'erreur, le follow a été créé avec succès
	}

	s.logger.Info("Playlist followed",
		zap.String("user_id", userID.String()),
		zap.String("playlist_id", playlistID.String()),
	)

	return nil
}

// UnfollowPlaylist supprime un follow d'un utilisateur sur une playlist
// MIGRATION UUID: Completée. userID et playlistID sont des UUIDs.
func (s *PlaylistFollowService) UnfollowPlaylist(ctx context.Context, userID uuid.UUID, playlistID uuid.UUID) error {
	// Vérifier si le follow existe
	var follow models.PlaylistFollow
	if err := s.db.WithContext(ctx).Where("user_id = ? AND playlist_id = ? AND deleted_at IS NULL", userID, playlistID).First(&follow).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			// Pas de follow à supprimer, retourner nil (idempotent)
			return nil
		}
		return fmt.Errorf("failed to check follow: %w", err)
	}

	// Supprimer le follow (soft delete)
	if err := s.db.WithContext(ctx).Delete(&follow).Error; err != nil {
		return fmt.Errorf("failed to delete follow: %w", err)
	}

	// Mettre à jour le compteur de followers de la playlist
	var playlist models.Playlist
	if err := s.db.WithContext(ctx).First(&playlist, "id = ?", playlistID).Error; err == nil {
		// Use CASE expression for SQLite compatibility (GREATEST is not supported in SQLite)
		if err := s.db.WithContext(ctx).Model(&playlist).UpdateColumn("follower_count", gorm.Expr("CASE WHEN follower_count - 1 < 0 THEN 0 ELSE follower_count - 1 END")).Error; err != nil {
			s.logger.Warn("Failed to update playlist follower_count",
				zap.String("playlist_id", playlistID.String()),
				zap.Error(err),
			)
			// Ne pas retourner l'erreur, le follow a été supprimé avec succès
		}
	}

	s.logger.Info("Playlist unfollowed",
		zap.String("user_id", userID.String()),
		zap.String("playlist_id", playlistID.String()),
	)

	return nil
}

// IsFollowing vérifie si un utilisateur suit une playlist
// MIGRATION UUID: Completée. userID et playlistID sont des UUIDs.
func (s *PlaylistFollowService) IsFollowing(ctx context.Context, userID uuid.UUID, playlistID uuid.UUID) (bool, error) {
	var count int64
	err := s.db.WithContext(ctx).Model(&models.PlaylistFollow{}).
		Where("user_id = ? AND playlist_id = ? AND deleted_at IS NULL", userID, playlistID).
		Count(&count).Error
	if err != nil {
		return false, fmt.Errorf("failed to check follow: %w", err)
	}
	return count > 0, nil
}

// GetPlaylistFollowersCount retourne le nombre de followers d'une playlist
func (s *PlaylistFollowService) GetPlaylistFollowersCount(ctx context.Context, playlistID uuid.UUID) (int64, error) {
	var count int64
	err := s.db.WithContext(ctx).Model(&models.PlaylistFollow{}).
		Where("playlist_id = ? AND deleted_at IS NULL", playlistID).
		Count(&count).Error
	if err != nil {
		return 0, fmt.Errorf("failed to get followers count: %w", err)
	}
	return count, nil
}

// GetFollowedPlaylists retourne toutes les playlists suivies par un utilisateur
// T0498: Create Playlist Recommendations
func (s *PlaylistFollowService) GetFollowedPlaylists(ctx context.Context, userID uuid.UUID) ([]*models.Playlist, error) {
	var playlists []*models.Playlist

	err := s.db.WithContext(ctx).
		Joins("INNER JOIN playlist_follows ON playlist_follows.playlist_id = playlists.id").
		Where("playlist_follows.user_id = ? AND playlist_follows.deleted_at IS NULL", userID).
		Preload("User").
		Preload("Tracks").
		Preload("Tracks.Track").
		Find(&playlists).Error

	if err != nil {
		return nil, fmt.Errorf("failed to get followed playlists: %w", err)
	}

	return playlists, nil
}
