package services

import (
	"context"
	"errors"
	"fmt"

	"veza-backend-api/internal/models"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

// CommentService gère les opérations sur les commentaires de tracks
type CommentService struct {
	db     *gorm.DB
	logger *zap.Logger
}

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

// CreateComment crée un nouveau commentaire sur un track
func (s *CommentService) CreateComment(ctx context.Context, trackID, userID int64, content string, parentID *int64) (*models.TrackComment, error) {
	// Vérifier que le track existe
	var track models.Track
	if err := s.db.WithContext(ctx).First(&track, trackID).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New("track not found")
		}
		return nil, fmt.Errorf("failed to check track: %w", err)
	}

	// Si parentID est fourni, vérifier que le commentaire parent existe
	if parentID != nil {
		var parent models.TrackComment
		if err := s.db.WithContext(ctx).First(&parent, *parentID).Error; err != nil {
			if err == gorm.ErrRecordNotFound {
				return nil, errors.New("parent comment not found")
			}
			return nil, fmt.Errorf("failed to check parent comment: %w", err)
		}
		// Vérifier que le parent appartient au même track
		if parent.TrackID != trackID {
			return nil, errors.New("parent comment does not belong to the same track")
		}
	}

	comment := &models.TrackComment{
		TrackID:  trackID,
		UserID:   userID,
		ParentID: parentID,
		Content:  content,
		IsEdited: false,
	}

	if err := s.db.WithContext(ctx).Create(comment).Error; err != nil {
		return nil, fmt.Errorf("failed to create comment: %w", err)
	}

	// Charger les relations
	if err := s.db.WithContext(ctx).Preload("User").Preload("Replies").First(comment, comment.ID).Error; err != nil {
		return nil, fmt.Errorf("failed to load comment relations: %w", err)
	}

	s.logger.Info("Comment created",
		zap.String("comment_id", comment.ID.String()),
		zap.Int64("track_id", trackID),
		zap.Int64("user_id", userID),
	)

	return comment, nil
}

// GetComments récupère les commentaires d'un track avec pagination
func (s *CommentService) GetComments(ctx context.Context, trackID int64, page, limit int) ([]*models.TrackComment, int64, error) {
	var comments []*models.TrackComment
	var total int64

	// Compter le total de commentaires racine (sans parent)
	query := s.db.WithContext(ctx).Model(&models.TrackComment{}).
		Where("track_id = ? AND parent_id IS NULL", trackID)

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, fmt.Errorf("failed to count comments: %w", err)
	}

	// Récupérer les commentaires avec pagination
	offset := (page - 1) * limit
	if offset < 0 {
		offset = 0
	}
	if limit <= 0 {
		limit = 20 // default limit
	}

	err := query.
		Preload("User").
		Preload("Replies", func(db *gorm.DB) *gorm.DB {
			return db.Preload("User").Order("created_at ASC")
		}).
		Order("created_at DESC").
		Offset(offset).
		Limit(limit).
		Find(&comments).Error

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

	return comments, total, nil
}

// UpdateComment met à jour un commentaire
func (s *CommentService) UpdateComment(ctx context.Context, commentID, userID int64, content string) (*models.TrackComment, error) {
	// Récupérer le commentaire
	var comment models.TrackComment
	if err := s.db.WithContext(ctx).First(&comment, commentID).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New("comment not found")
		}
		return nil, fmt.Errorf("failed to get comment: %w", err)
	}

	// Vérifier l'ownership
	if comment.UserID != userID {
		return nil, errors.New("unauthorized: you can only edit your own comments")
	}

	// Mettre à jour le commentaire
	comment.Content = content
	comment.IsEdited = true

	if err := s.db.WithContext(ctx).Save(&comment).Error; err != nil {
		return nil, fmt.Errorf("failed to update comment: %w", err)
	}

	// Charger les relations
	if err := s.db.WithContext(ctx).Preload("User").Preload("Replies").First(&comment, comment.ID).Error; err != nil {
		return nil, fmt.Errorf("failed to load comment relations: %w", err)
	}

	s.logger.Info("Comment updated",
		zap.Int64("comment_id", commentID),
		zap.Int64("user_id", userID),
	)

	return &comment, nil
}

// DeleteComment supprime un commentaire (soft delete)
func (s *CommentService) DeleteComment(ctx context.Context, commentID, userID int64) error {
	// Récupérer le commentaire
	var comment models.TrackComment
	if err := s.db.WithContext(ctx).First(&comment, commentID).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New("comment not found")
		}
		return fmt.Errorf("failed to get comment: %w", err)
	}

	// Vérifier l'ownership
	if comment.UserID != userID {
		return errors.New("unauthorized: you can only delete your own comments")
	}

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

	s.logger.Info("Comment deleted",
		zap.Int64("comment_id", commentID),
		zap.Int64("user_id", userID),
	)

	return nil
}

// GetReplies récupère les réponses d'un commentaire
func (s *CommentService) GetReplies(ctx context.Context, parentID int64, page, limit int) ([]*models.TrackComment, int64, error) {
	var replies []*models.TrackComment
	var total int64

	// Vérifier que le commentaire parent existe
	var parent models.TrackComment
	if err := s.db.WithContext(ctx).First(&parent, parentID).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, 0, errors.New("parent comment not found")
		}
		return nil, 0, fmt.Errorf("failed to check parent comment: %w", err)
	}

	// Compter le total de réponses
	query := s.db.WithContext(ctx).Model(&models.TrackComment{}).
		Where("parent_id = ?", parentID)

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, fmt.Errorf("failed to count replies: %w", err)
	}

	// Récupérer les réponses avec pagination
	offset := (page - 1) * limit
	if offset < 0 {
		offset = 0
	}
	if limit <= 0 {
		limit = 20 // default limit
	}

	err := query.
		Preload("User").
		Order("created_at ASC").
		Offset(offset).
		Limit(limit).
		Find(&replies).Error

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

	return replies, total, nil
}

