package repository

import (
	"api_yal/internal/models"
	"gorm.io/gorm"
)

// ratingRepositoryImpl реализация интерфейса RatingRepository
type ratingRepositoryImpl struct {
	db *gorm.DB
}

// NewRatingRepository создает новый экземпляр репозитория рейтингов
func NewRatingRepository(db *gorm.DB) RatingRepository {
	return &ratingRepositoryImpl{db: db}
}

// Create создает новый рейтинг
func (r *ratingRepositoryImpl) Create(rating *models.Rating) error {
	return r.db.Create(rating).Error
}

// GetByID возвращает рейтинг по ID
func (r *ratingRepositoryImpl) GetByID(id uint) (*models.Rating, error) {
	var rating models.Rating
	err := r.db.Preload("Object").Preload("VoteBreakdown").First(&rating, id).Error
	if err != nil {
		return nil, err
	}
	return &rating, nil
}

// GetByObjectAndPlatform возвращает рейтинг объекта для конкретной платформы
func (r *ratingRepositoryImpl) GetByObjectAndPlatform(objectID uint, platform models.PlatformType) (*models.Rating, error) {
	var rating models.Rating
	err := r.db.Preload("VoteBreakdown").Where("object_id = ? AND platform = ?", objectID, platform).First(&rating).Error
	if err != nil {
		return nil, err
	}
	return &rating, nil
}

// Update обновляет существующий рейтинг
func (r *ratingRepositoryImpl) Update(rating *models.Rating) error {
	return r.db.Save(rating).Error
}

// Delete удаляет рейтинг
func (r *ratingRepositoryImpl) Delete(id uint) error {
	return r.db.Delete(&models.Rating{}, id).Error
}

// List возвращает список рейтингов с пагинацией
func (r *ratingRepositoryImpl) List(offset, limit int) ([]models.Rating, error) {
	var ratings []models.Rating
	err := r.db.Preload("Object").Offset(offset).Limit(limit).Find(&ratings).Error
	if err != nil {
		return nil, err
	}
	return ratings, nil
}

// Count возвращает общее количество рейтингов
func (r *ratingRepositoryImpl) Count() (int64, error) {
	var count int64
	err := r.db.Model(&models.Rating{}).Count(&count).Error
	return count, err
}

// ListByObject возвращает все рейтинги объекта
func (r *ratingRepositoryImpl) ListByObject(objectID uint) ([]models.Rating, error) {
	var ratings []models.Rating
	err := r.db.Preload("VoteBreakdown").Where("object_id = ?", objectID).Find(&ratings).Error
	if err != nil {
		return nil, err
	}
	return ratings, nil
}

// ListByOwner возвращает рейтинги по владельцу
func (r *ratingRepositoryImpl) ListByOwner(ownerID uint, offset, limit int) ([]models.Rating, error) {
	var ratings []models.Rating
	err := r.db.Preload("Object").Where("owner_id = ?", ownerID).Offset(offset).Limit(limit).Find(&ratings).Error
	if err != nil {
		return nil, err
	}
	return ratings, nil
}

// ListByPlatform возвращает рейтинги по платформе
func (r *ratingRepositoryImpl) ListByPlatform(platform models.PlatformType, offset, limit int) ([]models.Rating, error) {
	var ratings []models.Rating
	err := r.db.Preload("Object").Where("platform = ?", platform).Offset(offset).Limit(limit).Find(&ratings).Error
	if err != nil {
		return nil, err
	}
	return ratings, nil
}

// GetObject возвращает объект, к которому относится рейтинг
func (r *ratingRepositoryImpl) GetObject(ratingID uint) (*models.Object, error) {
	var rating models.Rating
	err := r.db.Select("object_id").First(&rating, ratingID).Error
	if err != nil {
		return nil, err
	}
	return r.getObjectByID(rating.ObjectID)
}

// GetVoteBreakdown возвращает детализацию голосов для рейтинга
func (r *ratingRepositoryImpl) GetVoteBreakdown(ratingID uint) (*models.VoteBreakdown, error) {
	var breakdown models.VoteBreakdown
	err := r.db.Where("rating_id = ?", ratingID).First(&breakdown).Error
	if err != nil {
		return nil, err
	}
	return &breakdown, nil
}

// UpdateVoteBreakdown обновляет детализацию голосов
func (r *ratingRepositoryImpl) UpdateVoteBreakdown(breakdown *models.VoteBreakdown) error {
	return r.db.Save(breakdown).Error
}

// CreateRatingVote созд��ет новый голос в рейтинге
func (r *ratingRepositoryImpl) CreateRatingVote(vote *models.RatingVote) error {
	return r.db.Create(vote).Error
}

// GetRatingVote возвращает голос пользователя по цели и платформе
func (r *ratingRepositoryImpl) GetRatingVote(targetID uint, voterID uint, platform models.PlatformType) (*models.RatingVote, error) {
	var vote models.RatingVote
	err := r.db.Where("target_id = ? AND voter_id = ? AND platform = ?", targetID, voterID, platform).First(&vote).Error
	if err != nil {
		return nil, err
	}
	return &vote, nil
}

// UpdateRatingVote обновляет существующий голос
func (r *ratingRepositoryImpl) UpdateRatingVote(vote *models.RatingVote) error {
	return r.db.Save(vote).Error
}

// DeleteRatingVote удаляет голос
func (r *ratingRepositoryImpl) DeleteRatingVote(id uint) error {
	return r.db.Delete(&models.RatingVote{}, id).Error
}

// CountVotesByTarget возвращает количество голосов по цели и платформе
func (r *ratingRepositoryImpl) CountVotesByTarget(targetID uint, platform models.PlatformType) (int64, error) {
	var count int64
	err := r.db.Model(&models.RatingVote{}).Where("target_id = ? AND platform = ?", targetID, platform).Count(&count).Error
	return count, err
}

// CalculateAverageScore рассчитывает средний балл на основе VoteBreakdown
func (r *ratingRepositoryImpl) CalculateAverageScore(breakdown *models.VoteBreakdown) float64 {
	totalVotes := breakdown.Score1 + breakdown.Score2 + breakdown.Score3 + breakdown.Score4 + breakdown.Score5
	if totalVotes == 0 {
		return 0.0
	}
	totalScore := float64(breakdown.Score1*1 + breakdown.Score2*2 + breakdown.Score3*3 + breakdown.Score4*4 + breakdown.Score5*5)
	return totalScore / float64(totalVotes)
}

// Вспомогательный метод для получения объекта
func (r *ratingRepositoryImpl) getObjectByID(id uint) (*models.Object, error) {
	var object models.Object
	err := r.db.First(&object, id).Error
	if err != nil {
		return nil, err
	}
	return &object, nil
}