package repositories

import (
	config "astrology-api/configs"
	dto "astrology-api/dto"
	models "astrology-api/models/usermodel"

	"gorm.io/gorm"
)

type UserReviewRepository interface {
	AddReview(review *models.UserReview) error

	UpdateReview(review *models.UserReview) error

	DeleteReview(id uint) error

	GetReviewByID(id uint) (*models.UserReview, error)

	GetAstrologerReviews(astrologerID uint, startIndex, fetchRecord int) ([]dto.UserReviewResponse, int64, error)

	GetUserReviewHistory(userID uint) ([]dto.UserReviewResponse, error)

	ReplyReview(reviewID uint, reply string) error

	BlockReview(block *models.ReviewBlock) error

	IsReviewBlocked(reviewID uint, userID uint) (bool, error)

	GetBlockedReviews(userID uint) ([]dto.UserReviewResponse, error)

	GetAverageRating(astrologerID uint) (float64, error)

	GetTotalReviewCount(astrologerID uint) (int64, error)

	GetAdminReviewList(astrologerID uint) ([]dto.UserReviewResponse, error)
}

type userReviewRepository struct {
	db *gorm.DB
}

func NewUserReviewRepository() UserReviewRepository {
	return &userReviewRepository{
		db: config.DB,
	}
}

func (r *userReviewRepository) AddReview(review *models.UserReview) error {

	// Save review
	if err := r.db.Create(review).Error; err != nil {
		return err
	}

	// Reload review with relations
	return r.db.
		Preload("User").
		Preload("Astrologer").
		First(review, review.ID).Error
}

func (r *userReviewRepository) UpdateReview(review *models.UserReview) error {
	if err := r.db.Save(review).Error; err != nil {
		return err
	}
	return r.db.
		Preload("User").
		Preload("Astrologer").
		First(review, review.ID).Error

}

func (r *userReviewRepository) DeleteReview(id uint) error {

	return r.db.
		Model(&models.UserReview{}).
		Where("id = ?", id).
		Updates(map[string]interface{}{
			"isDelete": true,
			"isActive": false,
		}).Error
}

func (r *userReviewRepository) GetReviewByID(id uint) (*models.UserReview, error) {

	var review models.UserReview

	err := r.db.
		Model(&models.UserReview{}).
		Where("id = ?", id).
		Where("isDelete = ?", false).
		Where("isActive = ?", true).
		First(&review).Error

	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}

	return &review, nil
}

func (r *userReviewRepository) GetAstrologerReviews(

	astrologerID uint,

	startIndex int,

	fetchRecord int,

) ([]dto.UserReviewResponse, int64, error) {

	var list []dto.UserReviewResponse

	var total int64

	query := r.db.
		Table("user_reviews").
		Joins("JOIN users ON users.id=user_reviews.userId").
		Where("user_reviews.astrologerId=?", astrologerID)

	query.Count(&total)

	if fetchRecord > 0 {

		query = query.Offset(startIndex).Limit(fetchRecord)

	}

	err := query.Select(`user_reviews.id, user_reviews.rating, user_reviews.review, user_reviews.reply, user_reviews.created_at, users.id as userId, users.name,users.profile`).Scan(&list).Error

	if err != nil {

		return nil, 0, err

	}

	return list, total, nil

}

func (r *userReviewRepository) GetUserReviewHistory(userID uint) ([]dto.UserReviewResponse, error) {

	var list []dto.UserReviewResponse

	err := r.db.
		Table("user_reviews").
		Joins("JOIN astrologers ON astrologers.id=user_reviews.astrologerId").
		Where("user_reviews.userId=?", userID).
		Select(`user_reviews.id, user_reviews.rating, user_reviews.review, user_reviews.reply, user_reviews.created_at, astrologers.id as astrologerId, astrologers.name, astrologers.profileImage`).
		Scan(&list).Error

	if err != nil {

		return nil, err

	}

	return list, nil

}

func (r *userReviewRepository) ReplyReview(reviewID uint, reply string) error {

	return r.db.
		Model(&models.UserReview{}).
		Where("id = ?", reviewID).
		Update("reply", reply).Error
}

func (r *userReviewRepository) BlockReview(block *models.ReviewBlock) error {

	var count int64

	err := r.db.
		Model(&models.ReviewBlock{}).
		Where("userReviewId = ? AND userId = ?", block.UserReviewID, block.UserID).
		Count(&count).Error

	if err != nil {
		return err
	}

	if count > 0 {
		return nil
	}

	return r.db.Create(block).Error
}

func (r *userReviewRepository) IsReviewBlocked(reviewID uint, userID uint) (bool, error) {

	var count int64

	err := r.db.
		Model(&models.ReviewBlock{}).
		Where("userReviewId = ? AND userId = ?", reviewID, userID).
		Count(&count).Error

	if err != nil {
		return false, err
	}

	return count > 0, nil
}

func (r *userReviewRepository) GetBlockedReviews(
	userID uint,
) ([]dto.UserReviewResponse, error) {

	var reviews []dto.UserReviewResponse

	err := r.db.
		Table("review_blocks rb").
		Joins("JOIN user_reviews ur ON ur.id = rb.userReviewId").
		Joins("JOIN users u ON u.id = ur.userId").
		Where("rb.userId = ?", userID).
		Select(`
			ur.id,
			ur.rating,
			ur.review,
			ur.reply,
			ur.created_at,

			u.id as userId,
			u.name,
			u.profile
		`).
		Scan(&reviews).Error

	if err != nil {
		return nil, err
	}

	return reviews, nil
}

func (r *userReviewRepository) GetAverageRating(
	astrologerID uint,
) (float64, error) {

	var avg float64

	err := r.db.
		Model(&models.UserReview{}).
		Where("astrologerId = ?", astrologerID).
		Select("COALESCE(AVG(rating),0)").
		Scan(&avg).Error

	if err != nil {
		return 0, err
	}

	return avg, nil
}

func (r *userReviewRepository) GetTotalReviewCount(
	astrologerID uint,
) (int64, error) {

	var total int64

	err := r.db.
		Model(&models.UserReview{}).
		Where("astrologerId = ?", astrologerID).
		Count(&total).Error

	if err != nil {
		return 0, err
	}

	return total, nil
}

func (r *userReviewRepository) GetAdminReviewList(
	astrologerID uint,
) ([]dto.UserReviewResponse, error) {

	var list []dto.UserReviewResponse

	query := r.db.
		Table("user_reviews").
		Joins("JOIN users ON users.id = user_reviews.userId")

	if astrologerID > 0 {
		query = query.Where("user_reviews.astrologerId = ?", astrologerID)
	}

	err := query.
		Select(`
			user_reviews.id,
			user_reviews.rating,
			user_reviews.review,
			user_reviews.reply,
			user_reviews.isPublic,
			user_reviews.created_at,

			users.id as userId,
			users.name,
			users.profile
		`).
		Order("user_reviews.created_at DESC").
		Scan(&list).Error

	if err != nil {
		return nil, err
	}

	return list, nil
}
