package social

import (
	"context"
	"fmt"

	"github.com/google/uuid"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

// SocialService gère les interactions sociales
type SocialService interface {
	CreatePost(ctx context.Context, userID uuid.UUID, content string, attachments map[string]uuid.UUID) (*Post, error)
	GetGlobalFeed(ctx context.Context, limit, offset int) ([]FeedItem, error)
	GetUserFeed(ctx context.Context, userID uuid.UUID, limit, offset int) ([]FeedItem, error)

	GetPostsByUser(ctx context.Context, userID uuid.UUID, limit, offset int) ([]Post, error)

	// Interactions
	ToggleLike(ctx context.Context, userID uuid.UUID, targetID uuid.UUID, targetType string) (bool, error)
	AddComment(ctx context.Context, userID uuid.UUID, targetID uuid.UUID, targetType string, content string) (*Comment, error)

	// Internal
	CreateActivityPost(ctx context.Context, userID uuid.UUID, content string, meta map[string]interface{}) error
}

// Service implémente SocialService
type Service struct {
	db     *gorm.DB
	logger *zap.Logger
}

// NewService crée une nouvelle instance du service social
func NewService(db *gorm.DB, logger *zap.Logger) *Service {
	return &Service{
		db:     db,
		logger: logger,
	}
}

// CreatePost crée une nouvelle publication
func (s *Service) CreatePost(ctx context.Context, userID uuid.UUID, content string, attachments map[string]uuid.UUID) (*Post, error) {
	post := &Post{
		UserID:  userID,
		Content: content,
		Type:    PostTypeStatus,
	}

	// Handle attachments
	if trackID, ok := attachments["track_id"]; ok {
		post.TrackID = &trackID
		post.Type = PostTypeShare
	}
	if playlistID, ok := attachments["playlist_id"]; ok {
		post.PlaylistID = &playlistID
		post.Type = PostTypeShare
	}

	if err := s.db.Create(post).Error; err != nil {
		s.logger.Error("Failed to create post", zap.Error(err), zap.String("user_id", userID.String()))
		return nil, err
	}

	return post, nil
}

// GetGlobalFeed récupère un flux d'activité global
func (s *Service) GetGlobalFeed(ctx context.Context, limit, offset int) ([]FeedItem, error) {
	var posts []Post
	if err := s.db.Order("created_at desc").Limit(limit).Offset(offset).Find(&posts).Error; err != nil {
		return nil, err
	}

	var feed []FeedItem
	for _, p := range posts {
		targetType := "none"
		targetID := uuid.Nil

		if p.TrackID != nil {
			targetType = "track"
			targetID = *p.TrackID
		} else if p.PlaylistID != nil {
			targetType = "playlist"
			targetID = *p.PlaylistID
		}

		item := FeedItem{
			ID:         fmt.Sprintf("post:%s", p.ID.String()),
			Type:       ActivityPost,
			ActorID:    p.UserID,
			TargetID:   targetID,
			TargetType: targetType,
			Content:    p.Content,
			CreatedAt:  p.CreatedAt,
		}

		// Spécial pour les activités automatiques
		if p.Type == PostTypeActivity {
			item.Type = ActivityPurchase // Ou autre logique plus fine
		}

		feed = append(feed, item)
	}

	return feed, nil
}

// GetUserFeed récupère le flux d'un utilisateur (posts from people they follow + their own)
// NOTE: For now it just returns their own posts as FeedItems, keeping meaningful naming.
func (s *Service) GetUserFeed(ctx context.Context, userID uuid.UUID, limit, offset int) ([]FeedItem, error) {
	var posts []Post
	if err := s.db.Where("user_id = ?", userID).Order("created_at desc").Limit(limit).Offset(offset).Find(&posts).Error; err != nil {
		return nil, err
	}

	var feed []FeedItem
	for _, p := range posts {
		item := FeedItem{
			ID:         fmt.Sprintf("post:%s", p.ID.String()),
			Type:       ActivityPost,
			ActorID:    p.UserID,
			Content:    p.Content,
			CreatedAt:  p.CreatedAt,
			TargetType: "user_wall",
		}
		feed = append(feed, item)
	}

	return feed, nil
}

// GetPostsByUser récupère les posts d'un utilisateur spécifique
func (s *Service) GetPostsByUser(ctx context.Context, userID uuid.UUID, limit, offset int) ([]Post, error) {
	var posts []Post
	if err := s.db.Where("user_id = ?", userID).Order("created_at desc").Limit(limit).Offset(offset).Find(&posts).Error; err != nil {
		return nil, err
	}
	return posts, nil
}

// ToggleLike ajoute ou supprime un like
// Transactionnelle : SELECT like + DELETE/CREATE + UPDATE compteur dans une seule transaction
func (s *Service) ToggleLike(ctx context.Context, userID uuid.UUID, targetID uuid.UUID, targetType string) (bool, error) {
	var liked bool

	err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		// 1. VÉRIFICATION : Like existe déjà ? (SELECT dans la transaction)
		var like Like
		err := tx.Where("user_id = ? AND target_id = ? AND target_type = ?", userID, targetID, targetType).First(&like).Error

		if err == nil {
			// 2a. Mode UNLIKE : Like existe, on le supprime
			if err := tx.Delete(&like).Error; err != nil {
				return fmt.Errorf("ToggleLike: failed to delete like: %w", err)
			}

			// 3a. Décrémenter le compteur si c'est un post (dans la transaction)
			if targetType == "post" {
				if err := tx.Model(&Post{}).Where("id = ?", targetID).Update("like_count", gorm.Expr("like_count - 1")).Error; err != nil {
					return fmt.Errorf("ToggleLike: failed to decrement like_count: %w", err)
				}
			}

			liked = false
			return nil
		} else if err == gorm.ErrRecordNotFound {
			// 2b. Mode LIKE : Like n'existe pas, on le crée
			// Vérifier d'abord que la ressource existe (pour les posts)
			if targetType == "post" {
				var post Post
				if err := tx.Where("id = ?", targetID).First(&post).Error; err != nil {
					if err == gorm.ErrRecordNotFound {
						return fmt.Errorf("ToggleLike: post not found: %w", err)
					}
					return fmt.Errorf("ToggleLike: failed to check post existence: %w", err)
				}
			}

			like = Like{
				UserID:     userID,
				TargetID:   targetID,
				TargetType: targetType,
			}
			if err := tx.Create(&like).Error; err != nil {
				return fmt.Errorf("ToggleLike: failed to create like: %w", err)
			}

			// 3b. Incrémenter le compteur si c'est un post (dans la transaction)
			if targetType == "post" {
				if err := tx.Model(&Post{}).Where("id = ?", targetID).Update("like_count", gorm.Expr("like_count + 1")).Error; err != nil {
					return fmt.Errorf("ToggleLike: failed to increment like_count: %w", err)
				}
			}

			liked = true
			return nil
		} else {
			return fmt.Errorf("ToggleLike: failed to check like existence: %w", err)
		}
	})

	if err != nil {
		return false, err // Rollback automatique si erreur
	}

	return liked, nil
}

// AddComment ajoute un commentaire
// Transactionnelle : CREATE comment + UPDATE compteur dans une seule transaction
func (s *Service) AddComment(ctx context.Context, userID uuid.UUID, targetID uuid.UUID, targetType string, content string) (*Comment, error) {
	var comment *Comment

	err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		// 1. VALIDATION : Post existe ? (SELECT dans la transaction si targetType == "post")
		if targetType == "post" {
			var post Post
			if err := tx.First(&post, "id = ?", targetID).Error; err != nil {
				if err == gorm.ErrRecordNotFound {
					return fmt.Errorf("post not found")
				}
				return fmt.Errorf("AddComment: failed to validate post: %w", err)
			}
		}

		// 2. CRÉATION : Commentaire (INSERT dans la transaction)
		comment = &Comment{
			UserID:     userID,
			TargetID:   targetID,
			TargetType: targetType,
			Content:    content,
		}
		if err := tx.Create(comment).Error; err != nil {
			return fmt.Errorf("AddComment: failed to create comment: %w", err)
		}

		// 3. MISE À JOUR : Compteur (UPDATE dans la transaction)
		if targetType == "post" {
			if err := tx.Model(&Post{}).Where("id = ?", targetID).Update("comment_count", gorm.Expr("comment_count + 1")).Error; err != nil {
				return fmt.Errorf("AddComment: failed to increment comment_count: %w", err)
			}
		}

		// 4. RETOUR nil = commit automatique
		return nil
	})

	if err != nil {
		return nil, err // Rollback automatique si erreur
	}

	return comment, nil
}

// CreateActivityPost crée un post automatique pour une activité (ex: Achat)
func (s *Service) CreateActivityPost(ctx context.Context, userID uuid.UUID, content string, meta map[string]interface{}) error {
	post := &Post{
		UserID:  userID,
		Content: content,
		Type:    PostTypeActivity,
	}

	if trackIDStr, ok := meta["track_id"].(string); ok {
		if trackID, err := uuid.Parse(trackIDStr); err == nil {
			post.TrackID = &trackID
		}
	}

	return s.db.Create(post).Error
}
