// repositories/user_stats_repository.go
package repository

import (
	"time"

	"api_bb/internal/models"
	"api_bb/pkg/utils"
	"gorm.io/gorm"
)

type UserStatsRepository interface {
	Create(userStats *models.UserStats) error
	GetByID(id uint) (*models.UserStats, error)
	GetByUserID(userID uint) (*models.UserStats, error)
	Update(userStats *models.UserStats) error
	Delete(id uint) error
	UpdateStreaks(userID uint, lastWorkout time.Time) error
	UpdateWeeklyDistance(userID uint, distance float64) error
	UpdateMonthlyDistance(userID uint, distance float64) error
	IncrementWorkouts(userID uint, distance float64, duration int) error
	UpdatePersonalBest(userID uint, distanceType string, time string) error
	GetUserStatsResponse(userID uint) (*models.UserStatsResponse, error)
	GetByUserIDOrCreate(userID uint) (*models.UserStats, error)
}

type userStatsRepository struct {
	db *gorm.DB
}

func NewUserStatsRepository(db *gorm.DB) UserStatsRepository {
	return &userStatsRepository{db: db}
}

// GetByUserIDOrCreate возвращает статистику по ID пользователя или создает новую
func (r *userStatsRepository) GetByUserIDOrCreate(userID uint) (*models.UserStats, error) {
	userStats, err := r.GetByUserID(userID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			// Создаем новую статистику
			newStats := &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 := r.Create(newStats); err != nil {
				return nil, err
			}
			return newStats, nil
		}
		return nil, err
	}
	return userStats, nil
}

// Create создает новую статистику пользователя
func (r *userStatsRepository) Create(userStats *models.UserStats) error {
	return r.db.Create(userStats).Error
}

// GetByID возвращает статистику по ID
func (r *userStatsRepository) GetByID(id uint) (*models.UserStats, error) {
	var userStats models.UserStats
	err := r.db.First(&userStats, id).Error
	if err != nil {
		return nil, err
	}
	return &userStats, nil
}

// GetByUserID возвращает статистику по ID пользователя
func (r *userStatsRepository) GetByUserID(userID uint) (*models.UserStats, error) {
	var userStats models.UserStats
	err := r.db.Where("user_id = ?", userID).First(&userStats).Error
	if err != nil {
		return nil, err
	}
	return &userStats, nil
}

// Update обновляет статистику пользователя
func (r *userStatsRepository) Update(userStats *models.UserStats) error {
	return r.db.Save(userStats).Error
}

// Delete удаляет статистику пользователя
func (r *userStatsRepository) Delete(id uint) error {
	return r.db.Delete(&models.UserStats{}, id).Error
}

// UpdateStreaks обновляет серии тренировок
func (r *userStatsRepository) UpdateStreaks(userID uint, lastWorkout time.Time) error {
	userStats, err := r.GetByUserID(userID)
	if err != nil {
		return err
	}

	// Проверяем, была ли тренировка вчера
	yesterday := time.Now().AddDate(0, 0, -1)
	if userStats.LastWorkout.Format("2006-01-02") == yesterday.Format("2006-01-02") {
		// Продолжаем серию
		userStats.CurrentStreak++
	} else if userStats.LastWorkout.Format("2006-01-02") != time.Now().Format("2006-01-02") {
		// Сбрасываем серию, если не было тренировки сегодня или вчера
		userStats.CurrentStreak = 1
	}

	// Обновляем самую длинную серию
	if userStats.CurrentStreak > userStats.LongestStreak {
		userStats.LongestStreak = userStats.CurrentStreak
	}

	userStats.LastWorkout = lastWorkout

	return r.Update(userStats)
}

// UpdateWeeklyDistance обновляет недельный пробег
func (r *userStatsRepository) UpdateWeeklyDistance(userID uint, distance float64) error {
	return r.db.Model(&models.UserStats{}).
		Where("user_id = ?", userID).
		Update("weekly_distance", gorm.Expr("weekly_distance + ?", distance)).
		Error
}

// UpdateMonthlyDistance обновляет месячный пробег
func (r *userStatsRepository) UpdateMonthlyDistance(userID uint, distance float64) error {
	return r.db.Model(&models.UserStats{}).
		Where("user_id = ?", userID).
		Update("monthly_distance", gorm.Expr("monthly_distance + ?", distance)).
		Error
}

// IncrementWorkouts увеличивает счетчик тренировок и обновляет общие показатели
func (r *userStatsRepository) IncrementWorkouts(userID uint, distance float64, duration int) error {
	userStats, err := r.GetByUserID(userID)
	if err != nil {
		return err
	}

	// Обновляем общие показатели
	userStats.WorkoutsCount++
	userStats.TotalDistance += distance
	userStats.TotalTime += duration

	// Пересчитываем средний темп (в минутах на км)
	if userStats.TotalDistance > 0 {
		avgPaceMinPerKm := float64(userStats.TotalTime) / userStats.TotalDistance
		minutes := int(avgPaceMinPerKm)
		seconds := int((avgPaceMinPerKm - float64(minutes)) * 60)
		userStats.AvgPace = utils.FormatPace(minutes, seconds)
	}

	return r.Update(userStats)
}

// UpdatePersonalBest обновляет личный рекорд
func (r *userStatsRepository) UpdatePersonalBest(userID uint, distanceType string, time string) error {
	updateField := ""
	switch distanceType {
	case "5k":
		updateField = "best_5k"
	case "10k":
		updateField = "best_10k"
	case "half":
		updateField = "best_half"
	case "marathon":
		updateField = "best_marathon"
	default:
		return nil
	}

	return r.db.Model(&models.UserStats{}).
		Where("user_id = ?", userID).
		Update(updateField, time).
		Error
}

// GetUserStatsResponse возвращает статистику в формате DTO
func (r *userStatsRepository) GetUserStatsResponse(userID uint) (*models.UserStatsResponse, error) {
	userStats, err := r.GetByUserIDOrCreate(userID)
	if err != nil {
		return nil, err
	}

	return &models.UserStatsResponse{
		TotalDistance:   userStats.TotalDistance,
		TotalTime:       userStats.TotalTime,
		AvgPace:         userStats.AvgPace,
		WorkoutsCount:   userStats.WorkoutsCount,
		CurrentStreak:   userStats.CurrentStreak,
		LongestStreak:   userStats.LongestStreak,
		WeeklyDistance:  userStats.WeeklyDistance,
		MonthlyDistance: userStats.MonthlyDistance,
		PersonalBests: models.PersonalBestsSummary{
			Best5K:       userStats.Best5K,
			Best10K:      userStats.Best10K,
			BestHalf:     userStats.BestHalf,
			BestMarathon: userStats.BestMarathon,
		},
	}, nil
}
