// repository/review_repository.go
package repository

import (
	"api_bb/internal/models"
	"database/sql"
	"math"

	"gorm.io/gorm"
)

type ReviewRepository interface {
	Create(review *models.Review) error
	GetByID(id uint) (*models.Review, error)
	GetAll(page, limit int, sortBy, filter string) ([]models.Review, int64, error)
	GetByAuthorID(authorID uint) ([]models.Review, error)
	Update(review *models.Review) error
	Delete(id uint) error
	GetStats() (*models.ReviewsStatsResponse, error)
	GetRatingDistribution() (map[int]int, error)
}

type reviewRepository struct {
	db *gorm.DB
}

func NewReviewRepository(db *gorm.DB) ReviewRepository {
	return &reviewRepository{db: db}
}

func (r *reviewRepository) Create(review *models.Review) error {
	return r.db.Create(review).Error
}

func (r *reviewRepository) GetByID(id uint) (*models.Review, error) {
	var review models.Review
	err := r.db.Preload("Author").First(&review, id).Error
	return &review, err
}

func (r *reviewRepository) GetAll(page, limit int, sortBy, filter string) ([]models.Review, int64, error) {
	var reviews []models.Review
	var total int64

	query := r.db.Model(&models.Review{}).Preload("Author")

	// Применяем фильтрацию по рейтингу
	if filter != "" && filter != "all" {
		query = query.Where("rating >= ?", filter)
	}

	// Считаем общее количество
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// Применяем сортировку
	switch sortBy {
	case "newest":
		query = query.Order("created_at DESC")
	case "oldest":
		query = query.Order("created_at ASC")
	case "highest":
		query = query.Order("rating DESC, created_at DESC")
	case "lowest":
		query = query.Order("rating ASC, created_at DESC")
	default:
		query = query.Order("created_at DESC")
	}

	// Применяем пагинацию
	offset := (page - 1) * limit
	err := query.Offset(offset).Limit(limit).Find(&reviews).Error

	return reviews, total, err
}

func (r *reviewRepository) GetByAuthorID(authorID uint) ([]models.Review, error) {
	var reviews []models.Review
	err := r.db.Where("author_id = ?", authorID).Preload("Author").Find(&reviews).Error
	return reviews, err
}

func (r *reviewRepository) Update(review *models.Review) error {
	return r.db.Save(review).Error
}

func (r *reviewRepository) Delete(id uint) error {
	return r.db.Delete(&models.Review{}, id).Error
}

func (r *reviewRepository) GetStats() (*models.ReviewsStatsResponse, error) {
    var totalReviews int64
    var averageRating float64
    var successStories int64

    // Общее количество отзывов
    if err := r.db.Model(&models.Review{}).Count(&totalReviews).Error; err != nil {
        return nil, err
    }

    // Средний рейтинг - ИСПРАВЛЕННАЯ ЧАСТЬ
    var nullRating sql.NullFloat64
    if err := r.db.Model(&models.Review{}).Select("AVG(rating)").Row().Scan(&nullRating); err != nil {
        return nil, err
    }
    
    if nullRating.Valid {
        averageRating = nullRating.Float64
    } else {
        averageRating = 0
    }

    // Количество успешных историй (отзывы с рейтингом >= 4 и достижениями)
    if err := r.db.Model(&models.Review{}).
        Where("rating >= ? AND achievement != ?", 4, "").
        Count(&successStories).Error; err != nil {
        return nil, err
    }

    // Распределение по рейтингам
    ratingDistribution, err := r.GetRatingDistribution()
    if err != nil {
        return nil, err
    }

    return &models.ReviewsStatsResponse{
        TotalReviews:       int(totalReviews),
        AverageRating:      math.Round(averageRating*100) / 100, // Округляем до 2 знаков
        SuccessStories:     int(successStories),
        RatingDistribution: ratingDistribution,
    }, nil
}

func (r *reviewRepository) GetRatingDistribution() (map[int]int, error) {
	var results []struct {
		Rating int
		Count  int
	}

	err := r.db.Model(&models.Review{}).
		Select("rating, COUNT(*) as count").
		Group("rating").
		Order("rating DESC").
		Scan(&results).Error

	if err != nil {
		return nil, err
	}

	distribution := make(map[int]int)
	for _, result := range results {
		distribution[result.Rating] = result.Count
	}

	// Заполняем отсутствующие рейтинги нулями
	for i := 1; i <= 5; i++ {
		if _, exists := distribution[i]; !exists {
			distribution[i] = 0
		}
	}

	return distribution, nil
}
