package services

import (
	"context"
	"errors"
	"time"

	"github.com/google/uuid"
	"veza-backend-api/internal/models"

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

type CommentService struct {
	db     *gorm.DB
	logger *zap.Logger
}

func NewCommentService(db *gorm.DB, logger *zap.Logger) *CommentService {
	return &CommentService{
		db:     db,
		logger: logger,
	}
}

// CreateComment creates a new comment on a track
func (s *CommentService) CreateComment(ctx context.Context, trackID uuid.UUID, userID uuid.UUID, content string, timestamp float64, parentID *uuid.UUID) (*models.TrackComment, error) { // Updated trackID and parentID to uuid.UUID
	// Verify if track exists
	var track models.Track
	if err := s.db.WithContext(ctx).First(&track, "id = ?", trackID).Error; err != nil { // Updated query
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrTrackNotFound
		}
		return nil, err
	}

	// Verify if parent comment exists (if reply)
	if parentID != nil {
		var parent models.TrackComment
		if err := s.db.WithContext(ctx).First(&parent, "id = ?", *parentID).Error; err != nil { // Updated query
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return nil, ErrParentCommentNotFound
			}
			return nil, err
		}
		// Ensure parent belongs to the same track
		if parent.TrackID != trackID {
			return nil, ErrParentTrackMismatch
		}
	}

	comment := &models.TrackComment{
		TrackID:   trackID,
		UserID:    userID,
		Content:   content,
		Timestamp: timestamp,
		ParentID:  parentID,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	if err := s.db.WithContext(ctx).Create(comment).Error; err != nil {
		s.logger.Error("Failed to create comment", zap.Error(err))
		return nil, err
	}

	// Preload user info for response
	if err := s.db.WithContext(ctx).Preload("User").First(comment, comment.ID).Error; err != nil {
		return comment, nil // Return comment without user info if preload fails
	}

	s.logger.Info("Comment created",
		zap.Any("comment_id", comment.ID), // Changed to zap.Any for uuid.UUID
		zap.Any("track_id", trackID),      // Changed to zap.Any for uuid.UUID
		zap.String("user_id", userID.String()))

	return comment, nil
}

// GetComments retrieves comments for a track
func (s *CommentService) GetComments(ctx context.Context, trackID uuid.UUID, page, limit int) ([]models.TrackComment, int64, error) { // Updated trackID to uuid.UUID
	var comments []models.TrackComment
	var total int64

	offset := (page - 1) * limit

	// Count total top-level comments (or all comments? usually top-level for pagination, replies fetched separately or nested)
	// Here we fetch all top-level comments
	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, err
	}

	// Fetch comments with user info and replies
	// Note: Deep nesting of replies might require recursive query or multiple queries.
	// For simplicity, we just preload direct replies or let frontend handle threading if flat list.
	// Assuming flat list of top level + preloaded replies?
	// Let's just fetch top level and preload their replies one level deep for now
	err := query.
		Preload("User").
		Preload("Replies").
		Preload("Replies.User").
		Order("created_at DESC").
		Limit(limit).
		Offset(offset).
		Find(&comments).Error

	if err != nil {
		s.logger.Error("Failed to get comments", zap.Error(err))
		return nil, 0, err
	}

	return comments, total, nil
}

// UpdateComment updates a comment
func (s *CommentService) UpdateComment(ctx context.Context, commentID uuid.UUID, userID uuid.UUID, content string) (*models.TrackComment, error) { // Updated commentID to uuid.UUID
	var comment models.TrackComment
	if err := s.db.WithContext(ctx).First(&comment, "id = ?", commentID).Error; err != nil { // Updated query
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrCommentNotFound
		}
		return nil, err
	}

	// Check permission
	if comment.UserID != userID {
		return nil, ErrForbidden
	}

	comment.Content = content
	comment.IsEdited = true
	comment.UpdatedAt = time.Now()

	if err := s.db.WithContext(ctx).Save(&comment).Error; err != nil {
		s.logger.Error("Failed to update comment", zap.Error(err))
		return nil, err
	}

	s.logger.Info("Comment updated",
		zap.Any("comment_id", comment.ID), // Changed to zap.Any for uuid.UUID
		zap.String("user_id", userID.String()))

	return &comment, nil
}

// GetReplies retrieves replies for a given parent comment ID
func (s *CommentService) GetReplies(ctx context.Context, parentID uuid.UUID, page, limit int) ([]models.TrackComment, int64, error) { // Updated parentID to uuid.UUID
	var replies []models.TrackComment
	var total int64

	offset := (page - 1) * limit

	// Verify if parent comment exists
	var parent models.TrackComment
	if err := s.db.WithContext(ctx).First(&parent, "id = ?", parentID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, 0, ErrParentCommentNotFound
		}
		return nil, 0, err
	}

	// Count total replies
	query := s.db.WithContext(ctx).Model(&models.TrackComment{}).Where("parent_id = ?", parentID)

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// Fetch replies with user info
	err := query.
		Preload("User").
		Order("created_at ASC"). // Order by oldest first
		Limit(limit).
		Offset(offset).
		Find(&replies).Error

	if err != nil {
		s.logger.Error("Failed to get replies", zap.Error(err))
		return nil, 0, err
	}

	return replies, total, nil
}

// DeleteComment deletes a comment
// MIGRATION UUID: userID migré vers uuid.UUID, commentID reste int64
func (s *CommentService) DeleteComment(ctx context.Context, commentID uuid.UUID, userID uuid.UUID, isAdmin bool) error { // Updated commentID to uuid.UUID
	var comment models.TrackComment
	if err := s.db.WithContext(ctx).First(&comment, "id = ?", commentID).Error; err != nil { // Updated query
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return ErrCommentNotFound
		}
		return err
	}

	// Check permission
	if comment.UserID != userID && !isAdmin {
		return ErrForbidden
	}

	// Soft delete or hard delete? Model has DeletedAt so soft delete
	if err := s.db.WithContext(ctx).Delete(&comment).Error; err != nil {
		s.logger.Error("Failed to delete comment", zap.Error(err))
		return err
	}

	return nil
}
