// service/event_service.go
package service

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

	"go.uber.org/zap"
)

type EventService interface {
	CreateEvent(event *models.Event) error
	GetEventByID(id uint) (*models.Event, error)
	GetAllEvents() ([]models.Event, error)
	UpdateEvent(event *models.Event) error
	DeleteEvent(id uint) error
	GetEventsByType(eventType models.EventType) ([]models.Event, error)
	GetUpcomingEvents() ([]models.Event, error)
	GetEventsByDateRange(startDate, endDate time.Time) ([]models.Event, error)
	UpdateParticipantsCount(eventID uint) error
	ToggleRegistrationStatus(eventID uint, registrationOpen bool) error
}

type eventService struct {
	eventRepo        repository.EventRepository
	registrationRepo repository.EventRegistrationRepository
	logger           logger.LoggerInterface
}

func NewEventService(
	eventRepo repository.EventRepository,
	registrationRepo repository.EventRegistrationRepository,
	log logger.LoggerInterface,
) EventService {
	serviceLogger := log.With(zap.String("service", "event"))

	return &eventService{
		eventRepo:        eventRepo,
		registrationRepo: registrationRepo,
		logger:           serviceLogger,
	}
}

// CreateEvent создает новое событие
func (s *eventService) CreateEvent(event *models.Event) error {
	s.logger.Info("Creating new event",
		zap.String("title", event.Title),
		zap.String("type", string(event.Type)),
		zap.Time("date", event.Date),
	)

	if err := s.eventRepo.Create(event); err != nil {
		s.logger.Error("Failed to create event",
			zap.String("title", event.Title),
			zap.Error(err),
		)
		return fmt.Errorf("failed to create event: %w", err)
	}

	s.logger.Info("Event created successfully",
		zap.Uint("event_id", event.ID),
		zap.String("title", event.Title),
	)
	return nil
}

// GetEventByID возвращает событие по ID
func (s *eventService) GetEventByID(id uint) (*models.Event, error) {
	s.logger.Debug("Getting event by ID", zap.Uint("event_id", id))

	event, err := s.eventRepo.FindByID(id)
	if err != nil {
		s.logger.Warn("Event not found",
			zap.Uint("event_id", id),
			zap.Error(err),
		)
		return nil, fmt.Errorf("event not found: %w", err)
	}

	s.logger.Debug("Event retrieved successfully",
		zap.Uint("event_id", id),
		zap.String("title", event.Title),
	)
	return event, nil
}

// GetAllEvents возвращает все события
func (s *eventService) GetAllEvents() ([]models.Event, error) {
	s.logger.Debug("Getting all events")

	events, err := s.eventRepo.FindAll()
	if err != nil {
		s.logger.Error("Failed to get events", zap.Error(err))
		return nil, fmt.Errorf("failed to get events: %w", err)
	}

	s.logger.Debug("Events retrieved successfully",
		zap.Int("count", len(events)),
	)
	return events, nil
}

// UpdateEvent обновляет событие
func (s *eventService) UpdateEvent(event *models.Event) error {
	s.logger.Info("Updating event",
		zap.Uint("event_id", event.ID),
		zap.String("title", event.Title),
	)

	// Проверяем существование события
	existingEvent, err := s.eventRepo.FindByID(event.ID)
	if err != nil {
		s.logger.Warn("Event not found for update",
			zap.Uint("event_id", event.ID),
			zap.Error(err),
		)
		return fmt.Errorf("event not found")
	}

	// Сохраняем неизменяемые поля
	event.CreatedAt = existingEvent.CreatedAt
	event.UpdatedAt = time.Now()

	if err := s.eventRepo.Update(event); err != nil {
		s.logger.Error("Failed to update event",
			zap.Uint("event_id", event.ID),
			zap.Error(err),
		)
		return fmt.Errorf("failed to update event: %w", err)
	}

	s.logger.Info("Event updated successfully",
		zap.Uint("event_id", event.ID),
	)
	return nil
}

// DeleteEvent удаляет событие
func (s *eventService) DeleteEvent(id uint) error {
	s.logger.Info("Deleting event", zap.Uint("event_id", id))

	// Проверяем существование события
	_, err := s.eventRepo.FindByID(id)
	if err != nil {
		s.logger.Warn("Event not found for deletion",
			zap.Uint("event_id", id),
			zap.Error(err),
		)
		return fmt.Errorf("event not found")
	}

	if err := s.eventRepo.Delete(id); err != nil {
		s.logger.Error("Failed to delete event",
			zap.Uint("event_id", id),
			zap.Error(err),
		)
		return fmt.Errorf("failed to delete event: %w", err)
	}

	s.logger.Info("Event deleted successfully",
		zap.Uint("event_id", id),
	)
	return nil
}

// GetEventsByType возвращает события по типу
func (s *eventService) GetEventsByType(eventType models.EventType) ([]models.Event, error) {
	s.logger.Debug("Getting events by type", zap.String("type", string(eventType)))

	events, err := s.eventRepo.FindByType(eventType)
	if err != nil {
		s.logger.Error("Failed to get events by type",
			zap.String("type", string(eventType)),
			zap.Error(err),
		)
		return nil, fmt.Errorf("failed to get events by type: %w", err)
	}

	s.logger.Debug("Events by type retrieved successfully",
		zap.String("type", string(eventType)),
		zap.Int("count", len(events)),
	)
	return events, nil
}

// GetUpcomingEvents возвращает предстоящие события
func (s *eventService) GetUpcomingEvents() ([]models.Event, error) {
	s.logger.Debug("Getting upcoming events")

	events, err := s.eventRepo.FindUpcoming()
	if err != nil {
		s.logger.Error("Failed to get upcoming events", zap.Error(err))
		return nil, fmt.Errorf("failed to get upcoming events: %w", err)
	}

	s.logger.Debug("Upcoming events retrieved successfully",
		zap.Int("count", len(events)),
	)
	return events, nil
}

// GetEventsByDateRange возвращает события в диапазоне дат
func (s *eventService) GetEventsByDateRange(startDate, endDate time.Time) ([]models.Event, error) {
	s.logger.Debug("Getting events by date range",
		zap.Time("start_date", startDate),
		zap.Time("end_date", endDate),
	)

	events, err := s.eventRepo.FindByDateRange(startDate, endDate)
	if err != nil {
		s.logger.Error("Failed to get events by date range",
			zap.Time("start_date", startDate),
			zap.Time("end_date", endDate),
			zap.Error(err),
		)
		return nil, fmt.Errorf("failed to get events by date range: %w", err)
	}

	s.logger.Debug("Events by date range retrieved successfully",
		zap.Time("start_date", startDate),
		zap.Time("end_date", endDate),
		zap.Int("count", len(events)),
	)
	return events, nil
}

// UpdateParticipantsCount обновляет количество участников события
func (s *eventService) UpdateParticipantsCount(eventID uint) error {
	s.logger.Debug("Updating participants count", zap.Uint("event_id", eventID))

	count, err := s.registrationRepo.CountByEventID(eventID)
	if err != nil {
		s.logger.Error("Failed to count event registrations",
			zap.Uint("event_id", eventID),
			zap.Error(err),
		)
		return fmt.Errorf("failed to count registrations: %w", err)
	}

	if err := s.eventRepo.UpdateParticipantsCount(eventID, int(count)); err != nil {
		s.logger.Error("Failed to update participants count",
			zap.Uint("event_id", eventID),
			zap.Int64("count", count),
			zap.Error(err),
		)
		return fmt.Errorf("failed to update participants count: %w", err)
	}

	s.logger.Debug("Participants count updated successfully",
		zap.Uint("event_id", eventID),
		zap.Int64("count", count),
	)
	return nil
}

// ToggleRegistrationStatus переключает статус регистрации на событие
func (s *eventService) ToggleRegistrationStatus(eventID uint, registrationOpen bool) error {
	s.logger.Info("Toggling registration status",
		zap.Uint("event_id", eventID),
		zap.Bool("registration_open", registrationOpen),
	)

	if err := s.eventRepo.UpdateRegistrationStatus(eventID, registrationOpen); err != nil {
		s.logger.Error("Failed to toggle registration status",
			zap.Uint("event_id", eventID),
			zap.Bool("registration_open", registrationOpen),
			zap.Error(err),
		)
		return fmt.Errorf("failed to toggle registration status: %w", err)
	}

	s.logger.Info("Registration status updated successfully",
		zap.Uint("event_id", eventID),
		zap.Bool("registration_open", registrationOpen),
	)
	return nil
}
