// service/user_workout_service.go
package service

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

	"go.uber.org/zap"
)

type WorkoutService interface {
	CreateWorkout(userID uint, req *models.WorkoutCreateRequest) (*models.Workout, error)
	GetUserWorkouts(userID uint) ([]models.Workout, error)
	GetWorkoutByID(userID uint, workoutID uint) (*models.Workout, error)
	UpdateWorkout(userID uint, workoutID uint, req *models.WorkoutUpdateRequest) (*models.Workout, error)
	DeleteWorkout(userID uint, workoutID uint) error
	GetWorkoutStats(userID uint) (*models.WorkoutStatsResponse, error)
	GetWorkoutsByType(userID uint, workoutType models.WorkoutType) ([]models.Workout, error)
	GetLatestWorkouts(userID uint, limit int) ([]models.Workout, error)
}

type workoutService struct {
	workoutRepo repository.WorkoutRepository
	logger      logger.LoggerInterface
}

func NewWorkoutService(workoutRepo repository.WorkoutRepository) WorkoutService {
	return &workoutService{
		workoutRepo: workoutRepo,
		logger:      logger.NewWrapper(logger.Get().With(zap.String("service", "workout"))),
	}
}

// CreateWorkout создает новую тренировку
func (s *workoutService) CreateWorkout(userID uint, req *models.WorkoutCreateRequest) (*models.Workout, error) {
	s.logger.Info("creating new workout",
		zap.Uint("user_id", userID),
		zap.String("type", string(req.Type)),
		zap.Float64("distance", req.Distance),
	)

	// Создаем модель тренировки
	workout := &models.Workout{
		UserID:   userID,
		Type:     req.Type,
		Distance: req.Distance,
		Duration: req.Duration,
		Pace:     req.Pace,
		Calories: req.Calories,
		Notes:    req.Notes,
		Date:     req.Date,
	}

	// Сохраняем в репозитории
	if err := s.workoutRepo.Create(workout); err != nil {
		s.logger.Error("failed to create workout in repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return nil, err
	}

	s.logger.Info("workout created successfully",
		zap.Uint("workout_id", workout.ID),
		zap.Uint("user_id", userID),
	)

	return workout, nil
}

// GetUserWorkouts возвращает все тренировки пользователя
func (s *workoutService) GetUserWorkouts(userID uint) ([]models.Workout, error) {
	s.logger.Debug("getting user workouts", zap.Uint("user_id", userID))

	workouts, err := s.workoutRepo.FindByUserID(userID)
	if err != nil {
		s.logger.Error("failed to get user workouts from repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return nil, err
	}

	s.logger.Debug("retrieved user workouts",
		zap.Uint("user_id", userID),
		zap.Int("count", len(workouts)),
	)

	return workouts, nil
}

// GetWorkoutByID возвращает тренировку по ID
func (s *workoutService) GetWorkoutByID(userID uint, workoutID uint) (*models.Workout, error) {
	s.logger.Debug("getting workout by ID",
		zap.Uint("user_id", userID),
		zap.Uint("workout_id", workoutID),
	)

	workout, err := s.workoutRepo.FindByID(workoutID)
	if err != nil {
		s.logger.Error("failed to get workout from repository",
			zap.Uint("user_id", userID),
			zap.Uint("workout_id", workoutID),
			zap.Error(err),
		)
		return nil, err
	}

	// Проверяем, что тренировка принадлежит пользователю
	if workout.UserID != userID {
		s.logger.Warn("workout access denied - user mismatch",
			zap.Uint("user_id", userID),
			zap.Uint("workout_user_id", workout.UserID),
			zap.Uint("workout_id", workoutID),
		)
		return nil, repository.ErrNotFound
	}

	s.logger.Debug("workout retrieved successfully",
		zap.Uint("user_id", userID),
		zap.Uint("workout_id", workoutID),
	)

	return workout, nil
}

// UpdateWorkout обновляет тренировку
func (s *workoutService) UpdateWorkout(userID uint, workoutID uint, req *models.WorkoutUpdateRequest) (*models.Workout, error) {
	s.logger.Info("updating workout",
		zap.Uint("user_id", userID),
		zap.Uint("workout_id", workoutID),
	)

	// Сначала получаем существующую тренировку
	workout, err := s.GetWorkoutByID(userID, workoutID)
	if err != nil {
		return nil, err
	}

	// Обновляем только переданные поля
	if req.Type != "" {
		workout.Type = req.Type
	}
	if req.Distance > 0 {
		workout.Distance = req.Distance
	}
	if req.Duration > 0 {
		workout.Duration = req.Duration
	}
	if req.Pace != "" {
		workout.Pace = req.Pace
	}
	if req.Calories > 0 {
		workout.Calories = req.Calories
	}
	if req.Notes != "" {
		workout.Notes = req.Notes
	}
	if !req.Date.IsZero() {
		workout.Date = req.Date
	}

	// Сохраняем обновления
	if err := s.workoutRepo.Update(workout); err != nil {
		s.logger.Error("failed to update workout in repository",
			zap.Uint("user_id", userID),
			zap.Uint("workout_id", workoutID),
			zap.Error(err),
		)
		return nil, err
	}

	s.logger.Info("workout updated successfully",
		zap.Uint("user_id", userID),
		zap.Uint("workout_id", workoutID),
	)

	return workout, nil
}

// DeleteWorkout удаляет тренировку
func (s *workoutService) DeleteWorkout(userID uint, workoutID uint) error {
	s.logger.Info("deleting workout",
		zap.Uint("user_id", userID),
		zap.Uint("workout_id", workoutID),
	)

	// Проверяем, что тренировка существует и принадлежит пользователю
	workout, err := s.GetWorkoutByID(userID, workoutID)
	if err != nil {
		return err
	}

	// Удаляем тренировку
	if err := s.workoutRepo.Delete(workout.ID); err != nil {
		s.logger.Error("failed to delete workout from repository",
			zap.Uint("user_id", userID),
			zap.Uint("workout_id", workoutID),
			zap.Error(err),
		)
		return err
	}

	s.logger.Info("workout deleted successfully",
		zap.Uint("user_id", userID),
		zap.Uint("workout_id", workoutID),
	)

	return nil
}

// GetWorkoutStats возвращает статистику тренировок
func (s *workoutService) GetWorkoutStats(userID uint) (*models.WorkoutStatsResponse, error) {
	s.logger.Debug("getting workout stats", zap.Uint("user_id", userID))

	stats, err := s.workoutRepo.GetWorkoutStats(userID)
	if err != nil {
		s.logger.Error("failed to get workout stats from repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return nil, err
	}

	s.logger.Debug("workout stats retrieved successfully",
		zap.Uint("user_id", userID),
		zap.Int("total_workouts", stats.TotalWorkouts),
		zap.Float64("total_distance", stats.TotalDistance),
	)

	return stats, nil
}

// GetWorkoutsByType возвращает тренировки по типу
func (s *workoutService) GetWorkoutsByType(userID uint, workoutType models.WorkoutType) ([]models.Workout, error) {
	s.logger.Debug("getting workouts by type",
		zap.Uint("user_id", userID),
		zap.String("type", string(workoutType)),
	)

	workouts, err := s.workoutRepo.GetByType(userID, workoutType)
	if err != nil {
		s.logger.Error("failed to get workouts by type from repository",
			zap.Uint("user_id", userID),
			zap.String("type", string(workoutType)),
			zap.Error(err),
		)
		return nil, err
	}

	s.logger.Debug("workouts by type retrieved successfully",
		zap.Uint("user_id", userID),
		zap.String("type", string(workoutType)),
		zap.Int("count", len(workouts)),
	)

	return workouts, nil
}

// GetLatestWorkouts возвращает последние тренировки
func (s *workoutService) GetLatestWorkouts(userID uint, limit int) ([]models.Workout, error) {
	s.logger.Debug("getting latest workouts",
		zap.Uint("user_id", userID),
		zap.Int("limit", limit),
	)

	workouts, err := s.workoutRepo.GetLatestWorkouts(userID, limit)
	if err != nil {
		s.logger.Error("failed to get latest workouts from repository",
			zap.Uint("user_id", userID),
			zap.Int("limit", limit),
			zap.Error(err),
		)
		return nil, err
	}

	s.logger.Debug("latest workouts retrieved successfully",
		zap.Uint("user_id", userID),
		zap.Int("limit", limit),
		zap.Int("count", len(workouts)),
	)

	return workouts, nil
}