// service/training_plan_service.go
package service

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

	"go.uber.org/zap"
)

type TrainingPlanService interface {
	CreateTrainingPlan(userID uint, req *models.TrainingPlanCreateRequest) (*models.TrainingPlan, error)
	GetTrainingPlansByUserID(userID uint) ([]models.TrainingPlan, error)
	GetTrainingPlanByID(userID uint, planID uint) (*models.TrainingPlan, error)
	UpdateTrainingPlan(userID uint, planID uint, req *models.TrainingPlanUpdateRequest) (*models.TrainingPlan, error)
	DeleteTrainingPlan(userID uint, planID uint) error
	GetActiveTrainingPlan(userID uint) (*models.TrainingPlan, error)
	MarkTrainingPlanAsCompleted(userID uint, planID uint) error
	UpdateCurrentWeek(userID uint, planID uint, currentWeek int) error
}

type trainingPlanService struct {
	trainingPlanRepo repository.TrainingPlanRepository
	logger           logger.LoggerInterface
}

func NewTrainingPlanService(trainingPlanRepo repository.TrainingPlanRepository) TrainingPlanService {
	return &trainingPlanService{
		trainingPlanRepo: trainingPlanRepo,
		logger:           logger.NewWrapper(logger.Get().With(zap.String("service", "training_plan"))),
	}
}

// CreateTrainingPlan создает новый план тренировок
func (s *trainingPlanService) CreateTrainingPlan(userID uint, req *models.TrainingPlanCreateRequest) (*models.TrainingPlan, error) {
	s.logger.Debug("creating training plan",
		zap.Uint("user_id", userID),
		zap.String("title", req.Title),
	)

	plan := &models.TrainingPlan{
		UserID:          userID,
		Title:           req.Title,
		Description:     req.Description,
		Weeks:           req.Weeks,
		WorkoutsPerWeek: req.WorkoutsPerWeek,
		TargetDistance:  req.TargetDistance,
		TargetDate:      req.TargetDate,
		CurrentWeek:     1,
		Completed:       false,
	}

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

	s.logger.Debug("training plan created successfully",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", plan.ID),
	)

	return plan, nil
}

// GetTrainingPlansByUserID возвращает все планы тренировок пользователя
func (s *trainingPlanService) GetTrainingPlansByUserID(userID uint) ([]models.TrainingPlan, error) {
	s.logger.Debug("getting training plans for user", zap.Uint("user_id", userID))

	plans, err := s.trainingPlanRepo.GetByUserID(userID)
	if err != nil {
		s.logger.Error("failed to get training plans from repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return nil, err
	}

	s.logger.Debug("training plans retrieved successfully",
		zap.Uint("user_id", userID),
		zap.Int("count", len(plans)),
	)

	return plans, nil
}

// GetTrainingPlanByID возвращает план тренировок по ID
func (s *trainingPlanService) GetTrainingPlanByID(userID uint, planID uint) (*models.TrainingPlan, error) {
	s.logger.Debug("getting training plan by ID",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", planID),
	)

	plan, err := s.trainingPlanRepo.GetByID(planID)
	if err != nil {
		s.logger.Error("failed to get training plan from repository",
			zap.Uint("user_id", userID),
			zap.Uint("plan_id", planID),
			zap.Error(err),
		)
		return nil, err
	}

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

	s.logger.Debug("training plan retrieved successfully",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", planID),
	)

	return plan, nil
}

// UpdateTrainingPlan обновляет план тренировок
func (s *trainingPlanService) UpdateTrainingPlan(userID uint, planID uint, req *models.TrainingPlanUpdateRequest) (*models.TrainingPlan, error) {
	s.logger.Debug("updating training plan",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", planID),
	)

	// Сначала получаем существующий план
	plan, err := s.GetTrainingPlanByID(userID, planID)
	if err != nil {
		return nil, err
	}

	// Обновляем только переданные поля
	if req.Title != "" {
		plan.Title = req.Title
	}
	if req.Description != "" {
		plan.Description = req.Description
	}
	if req.Weeks > 0 {
		plan.Weeks = req.Weeks
	}
	if req.WorkoutsPerWeek > 0 {
		plan.WorkoutsPerWeek = req.WorkoutsPerWeek
	}
	if req.TargetDistance != "" {
		plan.TargetDistance = req.TargetDistance
	}
	if !req.TargetDate.IsZero() {
		plan.TargetDate = req.TargetDate
	}

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

	s.logger.Debug("training plan updated successfully",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", planID),
	)

	return plan, nil
}

// DeleteTrainingPlan удаляет план тренировок
func (s *trainingPlanService) DeleteTrainingPlan(userID uint, planID uint) error {
	s.logger.Debug("deleting training plan",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", planID),
	)

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

	// Удаляем план
	if err := s.trainingPlanRepo.Delete(planID); err != nil {
		s.logger.Error("failed to delete training plan from repository",
			zap.Uint("user_id", userID),
			zap.Uint("plan_id", planID),
			zap.Error(err),
		)
		return err
	}

	s.logger.Debug("training plan deleted successfully",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", planID),
	)

	return nil
}

// GetActiveTrainingPlan возвращает активный план тренировок пользователя
func (s *trainingPlanService) GetActiveTrainingPlan(userID uint) (*models.TrainingPlan, error) {
	s.logger.Debug("getting active training plan for user", zap.Uint("user_id", userID))

	plan, err := s.trainingPlanRepo.GetActivePlan(userID)
	if err != nil {
		s.logger.Error("failed to get active training plan from repository",
			zap.Uint("user_id", userID),
			zap.Error(err),
		)
		return nil, err
	}

	s.logger.Debug("active training plan retrieved successfully",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", plan.ID),
	)

	return plan, nil
}

// MarkTrainingPlanAsCompleted помечает план тренировок как завершенный
func (s *trainingPlanService) MarkTrainingPlanAsCompleted(userID uint, planID uint) error {
	s.logger.Debug("marking training plan as completed",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", planID),
	)

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

	// Помечаем как завершенный
	if err := s.trainingPlanRepo.MarkAsCompleted(planID); err != nil {
		s.logger.Error("failed to mark training plan as completed in repository",
			zap.Uint("user_id", userID),
			zap.Uint("plan_id", planID),
			zap.Error(err),
		)
		return err
	}

	s.logger.Debug("training plan marked as completed successfully",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", planID),
	)

	return nil
}

// UpdateCurrentWeek обновляет текущую неделю плана тренировок
func (s *trainingPlanService) UpdateCurrentWeek(userID uint, planID uint, currentWeek int) error {
	s.logger.Debug("updating current week for training plan",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", planID),
		zap.Int("current_week", currentWeek),
	)

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

	// Обновляем текущую неделю
	if err := s.trainingPlanRepo.UpdateCurrentWeek(planID, currentWeek); err != nil {
		s.logger.Error("failed to update current week in repository",
			zap.Uint("user_id", userID),
			zap.Uint("plan_id", planID),
			zap.Error(err),
		)
		return err
	}

	s.logger.Debug("current week updated successfully",
		zap.Uint("user_id", userID),
		zap.Uint("plan_id", planID),
		zap.Int("current_week", currentWeek),
	)

	return nil
}