// service/user_stats_service.go
package service

import (
	"time"

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

	"go.uber.org/zap"
)

type UserStatsService interface {
	GetUserStats(userID uint) (*models.UserStatsResponse, error)
	UpdatePersonalBest(userID uint, distanceType string, time string) error
	IncrementWorkout(userID uint, distance float64, duration int) error
	ResetWeeklyDistance(userID uint) error
	ResetMonthlyDistance(userID uint) error
	CreateUserStats(userID uint) error
}

type userStatsService struct {
	logger        logger.LoggerInterface
	userStatsRepo repository.UserStatsRepository
}

func NewUserStatsService(userStatsRepo repository.UserStatsRepository) UserStatsService {
	return &userStatsService{
		logger:        logger.NewWrapper(logger.Get().With(zap.String("service", "user_stats"))),
		userStatsRepo: userStatsRepo,
	}
}

// GetUserStats возвращает статистику пользователя в формате DTO
func (s *userStatsService) GetUserStats(userID uint) (*models.UserStatsResponse, error) {
	s.logger.Info("getting user stats",
		zap.Uint("user_id", userID),
	)

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

	s.logger.Debug("user stats retrieved successfully",
		zap.Uint("user_id", userID),
		zap.Float64("total_distance", stats.TotalDistance),
		zap.Int("workouts_count", stats.WorkoutsCount),
	)

	return stats, nil
}

// UpdatePersonalBest обновляет личный рекорд пользователя
func (s *userStatsService) UpdatePersonalBest(userID uint, distanceType string, time string) error {
	s.logger.Info("updating personal best",
		zap.Uint("user_id", userID),
		zap.String("distance_type", distanceType),
		zap.String("time", time),
	)

	// Используем GetByUserIDOrCreate вместо проверки существования
	_, err := s.userStatsRepo.GetByUserIDOrCreate(userID)
	if err != nil {
		s.logger.Error("failed to get or create user stats",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return err
	}

	if err := s.userStatsRepo.UpdatePersonalBest(userID, distanceType, time); err != nil {
		s.logger.Error("failed to update personal best in repository",
			zap.Uint("user_id", userID),
			zap.String("distance_type", distanceType),
			zap.Error(err),
		)
		return err
	}

	s.logger.Info("personal best updated successfully",
		zap.Uint("user_id", userID),
		zap.String("distance_type", distanceType),
		zap.String("time", time),
	)

	return nil
}

// IncrementWorkout увеличивает счетчик тренировок и обновляет статистику
func (s *userStatsService) IncrementWorkout(userID uint, distance float64, duration int) error {
	s.logger.Info("incrementing workout stats",
		zap.Uint("user_id", userID),
		zap.Float64("distance", distance),
		zap.Int("duration", duration),
	)

	// Используем GetByUserIDOrCreate для гарантии существования статистики
	_, err := s.userStatsRepo.GetByUserIDOrCreate(userID)
	if err != nil {
		s.logger.Error("failed to get or create user stats",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return err
	}

	// Обновляем серии тренировок
	currentTime := time.Now()
	if err := s.userStatsRepo.UpdateStreaks(userID, currentTime); err != nil {
		s.logger.Error("failed to update streaks in repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return err
	}

	// Обновляем недельный и месячный пробег
	if err := s.userStatsRepo.UpdateWeeklyDistance(userID, distance); err != nil {
		s.logger.Error("failed to update weekly distance in repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return err
	}

	if err := s.userStatsRepo.UpdateMonthlyDistance(userID, distance); err != nil {
		s.logger.Error("failed to update monthly distance in repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return err
	}

	// Увеличиваем счетчик тренировок и обновляем общие показатели
	if err := s.userStatsRepo.IncrementWorkouts(userID, distance, duration); err != nil {
		s.logger.Error("failed to increment workouts in repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return err
	}

	s.logger.Info("workout stats incremented successfully",
		zap.Uint("user_id", userID),
		zap.Float64("distance", distance),
		zap.Int("duration", duration),
	)

	return nil
}

// ResetWeeklyDistance сбрасывает недельный пробег
func (s *userStatsService) ResetWeeklyDistance(userID uint) error {
	s.logger.Info("resetting weekly distance",
		zap.Uint("user_id", userID),
	)

	userStats, err := s.userStatsRepo.GetByUserID(userID)
	if err != nil {
		s.logger.Error("failed to get user stats for weekly reset",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return err
	}

	userStats.WeeklyDistance = 0
	if err := s.userStatsRepo.Update(userStats); err != nil {
		s.logger.Error("failed to reset weekly distance in repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return err
	}

	s.logger.Info("weekly distance reset successfully",
		zap.Uint("user_id", userID),
	)

	return nil
}

// ResetMonthlyDistance сбрасывает месячный пробег
func (s *userStatsService) ResetMonthlyDistance(userID uint) error {
	s.logger.Info("resetting monthly distance",
		zap.Uint("user_id", userID),
	)

	userStats, err := s.userStatsRepo.GetByUserID(userID)
	if err != nil {
		s.logger.Error("failed to get user stats for monthly reset",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return err
	}

	userStats.MonthlyDistance = 0
	if err := s.userStatsRepo.Update(userStats); err != nil {
		s.logger.Error("failed to reset monthly distance in repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return err
	}

	s.logger.Info("monthly distance reset successfully",
		zap.Uint("user_id", userID),
	)

	return nil
}

// CreateUserStats создает начальную статистику для пользователя
func (s *userStatsService) CreateUserStats(userID uint) error {
	s.logger.Info("creating user stats",
		zap.Uint("user_id", userID),
	)

	userStats := &models.UserStats{
		UserID:          userID,
		TotalDistance:   0,
		TotalTime:       0,
		AvgPace:         "0:00",
		WorkoutsCount:   0,
		CurrentStreak:   0,
		LongestStreak:   0,
		WeeklyDistance:  0,
		MonthlyDistance: 0,
		Best5K:          "",
		Best10K:         "",
		BestHalf:        "",
		BestMarathon:    "",
		LastWorkout:     time.Time{},
	}

	if err := s.userStatsRepo.Create(userStats); err != nil {
		s.logger.Error("failed to create user stats in repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return err
	}

	s.logger.Info("user stats created successfully",
		zap.Uint("user_id", userID),
	)

	return nil
}
