// service/review_service.go
package service

import (
	"api_bb/internal/models"
	"api_bb/internal/repository"
	"api_bb/pkg/logger"
	"errors"

	"go.uber.org/zap"
)

type ReviewService interface {
	CreateReview(req *models.CreateReviewRequest, authorID uint) (*models.ReviewResponse, error)
	GetReviewByID(id uint) (*models.ReviewResponse, error)
	GetAllReviews(page, limit int, sortBy, filter string) ([]models.ReviewResponse, int, error)
	GetUserReviews(userID uint) ([]models.ReviewResponse, error)
	UpdateReview(id uint, req *models.UpdateReviewRequest, userID uint, isAdmin bool) (*models.ReviewResponse, error)
	DeleteReview(id uint, userID uint, isAdmin bool) error
	GetReviewsStats() (*models.ReviewsStatsResponse, error)
}

type reviewService struct {
	reviewRepo repository.ReviewRepository
	logger     logger.LoggerInterface
}

func NewReviewService(reviewRepo repository.ReviewRepository, logger logger.LoggerInterface) ReviewService {
	return &reviewService{
		reviewRepo: reviewRepo,
		logger:     logger,
	}
}

func (s *reviewService) CreateReview(req *models.CreateReviewRequest, authorID uint) (*models.ReviewResponse, error) {
	review := &models.Review{
		Rating:      req.Rating,
		Text:        req.Text,
		Achievement: req.Achievement,
		Distance:    req.Distance,
		Improvement: req.Improvement,
		Trainings:   req.Trainings,
		AuthorID:    authorID,
		Verified:    false, // По умолчанию непроверенный
	}

	if err := s.reviewRepo.Create(review); err != nil {
		s.logger.Error("Failed to create review", zap.Error(err))
		return nil, err
	}

	// Получаем созданный отзыв с информацией об авторе
	createdReview, err := s.reviewRepo.GetByID(review.ID)
	if err != nil {
		s.logger.Error("Failed to get created review", zap.Error(err))
		return nil, err
	}

	return s.toReviewResponse(createdReview), nil
}

func (s *reviewService) GetReviewByID(id uint) (*models.ReviewResponse, error) {
	review, err := s.reviewRepo.GetByID(id)
	if err != nil {
		s.logger.With(zap.Int("id", int(id))).Error("Failed to get review by ID", zap.Error(err))
		return nil, err
	}

	return s.toReviewResponse(review), nil
}

func (s *reviewService) GetAllReviews(page, limit int, sortBy, filter string) ([]models.ReviewResponse, int, error) {
	if page < 1 {
		page = 1
	}
	if limit < 1 {
		limit = 10
	}

	reviews, total, err := s.reviewRepo.GetAll(page, limit, sortBy, filter)
	if err != nil {
		s.logger.Error("Failed to get all reviews", zap.Error(err))
		return nil, 0, err
	}

	responses := make([]models.ReviewResponse, len(reviews))
	for i, review := range reviews {
		responses[i] = *s.toReviewResponse(&review)
	}

	totalPages := (int(total) + limit - 1) / limit

	return responses, totalPages, nil
}

func (s *reviewService) GetUserReviews(userID uint) ([]models.ReviewResponse, error) {
	reviews, err := s.reviewRepo.GetByAuthorID(userID)
	if err != nil {
		s.logger.With(zap.Int("userID", int(userID))).Error("Failed to get user reviews", zap.Error(err))
		return nil, err
	}

	responses := make([]models.ReviewResponse, len(reviews))
	for i, review := range reviews {
		responses[i] = *s.toReviewResponse(&review)
	}

	return responses, nil
}

func (s *reviewService) UpdateReview(id uint, req *models.UpdateReviewRequest, userID uint, isAdmin bool) (*models.ReviewResponse, error) {
	review, err := s.reviewRepo.GetByID(id)
	if err != nil {
		s.logger.With(zap.Int("id", int(id))).Error("Failed to get review for update", zap.Error(err))
		return nil, err
	}

	// Проверяем права доступа
	if review.AuthorID != userID && !isAdmin {
		s.logger.With(zap.Int("userID", int(userID))).With(zap.Int("reviewAuthorID", int(review.AuthorID))).Error("Unauthorized attempt to update review", zap.Error(err))
	}

	// Обновляем поля
	if req.Rating != 0 {
		review.Rating = req.Rating
	}
	if req.Text != "" {
		review.Text = req.Text
	}
	if req.Achievement != "" {
		review.Achievement = req.Achievement
	}
	if req.Distance != "" {
		review.Distance = req.Distance
	}
	if req.Improvement != "" {
		review.Improvement = req.Improvement
	}
	if req.Trainings != 0 {
		review.Trainings = req.Trainings
	}

	if err := s.reviewRepo.Update(review); err != nil {
		s.logger.With(zap.Int("id", int(id))).Error("Failed to update review", zap.Error(err))
		return nil, err
	}

	// Получаем обновленный отзыв
	updatedReview, err := s.reviewRepo.GetByID(id)
	if err != nil {
		s.logger.With(zap.Int("id", int(id))).Error("Failed to get updated review", zap.Error(err))
		return nil, err
	}

	return s.toReviewResponse(updatedReview), nil
}

func (s *reviewService) DeleteReview(id uint, userID uint, isAdmin bool) error {
	review, err := s.reviewRepo.GetByID(id)
	if err != nil {
		s.logger.With(zap.Int("id", int(id))).Error("Failed to get review for deletion", zap.Error(err))
		return err
	}

	// Проверяем права доступа
	if review.AuthorID != userID && !isAdmin {
		s.logger.With(zap.Int("userID", int(userID))).With(zap.Int("reviewAuthorID", int(review.AuthorID))).Error("Unauthorized attempt to delete review", zap.Error(err))
		return errors.New("unauthorized")
	}

	return s.reviewRepo.Delete(id)
}

func (s *reviewService) GetReviewsStats() (*models.ReviewsStatsResponse, error) {
	return s.reviewRepo.GetStats()
}

func (s *reviewService) toReviewResponse(review *models.Review) *models.ReviewResponse {
	return &models.ReviewResponse{
		ID:          review.ID,
		CreatedAt:   review.CreatedAt,
		Rating:      review.Rating,
		Text:        review.Text,
		Achievement: review.Achievement,
		Distance:    review.Distance,
		Improvement: review.Improvement,
		Trainings:   review.Trainings,
		Verified:    review.Verified,
		Author: models.AuthorInfo{
			ID:        review.Author.ID,
			FirstName: review.Author.FirstName,
			LastName:  review.Author.LastName,
			Email:     review.Author.Email,
		},
	}
}
