// handlers/event_registration_handler.go
package handlers

import (
	"api_bb/internal/models"
	"api_bb/internal/service"
	"api_bb/pkg/logger"
	"api_bb/pkg/middleware"
	"api_bb/pkg/utils"
	"encoding/json"
	"net/http"
	"strconv"

	"go.uber.org/zap"
)

type EventRegistrationHandler struct {
	logger                    logger.LoggerInterface
	registrationService       service.EventRegistrationService
}

func NewEventRegistrationHandler(registrationService service.EventRegistrationService) *EventRegistrationHandler {
	return &EventRegistrationHandler{
		logger:              logger.NewWrapper(logger.Get().With(zap.String("handler", "event_registration"))),
		registrationService: registrationService,
	}
}

// RegisterForEventRequest - DTO для регистрации на событие
type RegisterForEventRequest struct {
	EventID uint   `json:"event_id" validate:"required"`
	Notes   string `json:"notes" validate:"max=500"`
}

// UpdateRegistrationRequest - DTO для обновления регистрации
type UpdateRegistrationRequest struct {
	Notes string `json:"notes" validate:"max=500"`
}

// RegistrationResponse - DTO для ответа с регистрацией
type RegistrationResponse struct {
	ID        uint      `json:"id"`
	UserID    uint      `json:"user_id"`
	EventID   uint      `json:"event_id"`
	Status    string    `json:"status"`
	Notes     string    `json:"notes"`
	ResultTime string   `json:"result_time"`
	CreatedAt string    `json:"created_at"`
	UpdatedAt string    `json:"updated_at"`
	Event     EventResponse `json:"event,omitempty"`
}

// RegisterForEvent регистрирует пользователя на событие
func (h *EventRegistrationHandler) RegisterForEvent(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling register for event request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Проверяем аутентификацию
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("register for event failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	var req RegisterForEventRequest
	if err := utils.DecodeJSONBody(w, r, &req); err != nil {
		h.logger.Error("failed to decode request body", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
		return
	}

	// Валидация
	if err := utils.ValidateStruct(req); err != nil {
		h.logger.Warn("validation failed for register for event", zap.Error(err))
		utils.RespondWithValidationError(w, err)
		return
	}

	// Создаем модель регистрации
	registration := &models.EventRegistration{
		UserID:  user.ID,
		EventID: req.EventID,
		Status:  "pending",
		Notes:   req.Notes,
	}

	if err := h.registrationService.RegisterForEvent(registration); err != nil {
		h.logger.Error("failed to register for event",
			zap.Uint("user_id", user.ID),
			zap.Uint("event_id", req.EventID),
			zap.Error(err),
		)
		statusCode := http.StatusInternalServerError
		if err.Error() == "event not found" {
			statusCode = http.StatusNotFound
		} else if err.Error() == "user already registered for this event" {
			statusCode = http.StatusConflict
		} else if err.Error() == "registration is closed for this event" {
			statusCode = http.StatusForbidden
		} else if err.Error() == "event is full" {
			statusCode = http.StatusConflict
		}
		utils.RespondWithError(w, statusCode, "Failed to register for event: "+err.Error())
		return
	}

	h.logger.Info("user registered for event successfully",
		zap.Uint("user_id", user.ID),
		zap.Uint("event_id", req.EventID),
		zap.Uint("registration_id", registration.ID),
	)

	utils.RespondWithJSON(w, http.StatusCreated, map[string]interface{}{
		"message": "Successfully registered for event",
		"registration": toRegistrationResponse(registration),
	})
}

// GetRegistration возвращает регистрацию по ID
func (h *EventRegistrationHandler) GetRegistration(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get registration request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Проверяем аутентификацию
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("get registration failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Извлекаем ID регистрации
	registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
	if err != nil {
		h.logger.Warn("invalid registration ID", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID")
		return
	}

	registration, err := h.registrationService.GetRegistrationByID(uint(registrationID))
	if err != nil {
		h.logger.Warn("registration not found",
			zap.Uint("registration_id", uint(registrationID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusNotFound, "Registration not found")
		return
	}

	// Проверяем права доступа (пользователь может видеть только свои регистрации, админ - все)
	if user.Role != "admin" && registration.UserID != user.ID {
		h.logger.Warn("access denied to registration",
			zap.Uint("user_id", user.ID),
			zap.Uint("registration_user_id", registration.UserID),
		)
		utils.RespondWithError(w, http.StatusForbidden, "Access denied")
		return
	}

	h.logger.Info("registration retrieved successfully",
		zap.Uint("registration_id", uint(registrationID)),
		zap.Uint("user_id", user.ID),
	)

	utils.RespondWithJSON(w, http.StatusOK, toRegistrationResponse(registration))
}

// GetUserRegistrations возвращает все регистрации пользователя
func (h *EventRegistrationHandler) GetUserRegistrations(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get user registrations request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Проверяем аутентификацию
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("get user registrations failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	registrations, err := h.registrationService.GetRegistrationsByUserID(user.ID)
	if err != nil {
		h.logger.Error("failed to get user registrations",
			zap.Uint("user_id", user.ID),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get registrations: "+err.Error())
		return
	}

	var registrationResponses []RegistrationResponse
	for _, registration := range registrations {
		registrationResponses = append(registrationResponses, toRegistrationResponse(&registration))
	}

	h.logger.Info("user registrations retrieved successfully",
		zap.Uint("user_id", user.ID),
		zap.Int("registrations_count", len(registrationResponses)),
	)

	utils.RespondWithJSON(w, http.StatusOK, registrationResponses)
}

// GetEventRegistrations возвращает все регистрации на событие
func (h *EventRegistrationHandler) GetEventRegistrations(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling get event registrations request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Проверяем аутентификацию и права (только админы могут видеть все регистрации на событие)
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("get event registrations failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	if user.Role != "admin" {
		h.logger.Warn("get event registrations failed - insufficient permissions",
			zap.Uint("user_id", user.ID),
			zap.String("user_role", user.Role),
		)
		utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
		return
	}

	// Извлекаем ID события
	eventID, err := strconv.ParseUint(r.PathValue("eventId"), 10, 32)
	if err != nil {
		h.logger.Warn("invalid event ID", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid event ID")
		return
	}

	registrations, err := h.registrationService.GetRegistrationsByEventID(uint(eventID))
	if err != nil {
		h.logger.Error("failed to get event registrations",
			zap.Uint("event_id", uint(eventID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get registrations: "+err.Error())
		return
	}

	var registrationResponses []RegistrationResponse
	for _, registration := range registrations {
		registrationResponses = append(registrationResponses, toRegistrationResponse(&registration))
	}

	h.logger.Info("event registrations retrieved successfully",
		zap.Uint("event_id", uint(eventID)),
		zap.Int("registrations_count", len(registrationResponses)),
	)

	utils.RespondWithJSON(w, http.StatusOK, registrationResponses)
}

// CancelRegistration отменяет регистрацию
func (h *EventRegistrationHandler) CancelRegistration(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling cancel registration request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Проверяем аутентификацию
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("cancel registration failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	// Извлекаем ID регистрации
	registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
	if err != nil {
		h.logger.Warn("invalid registration ID", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID")
		return
	}

	// Проверяем права доступа
	registration, err := h.registrationService.GetRegistrationByID(uint(registrationID))
	if err != nil {
		h.logger.Warn("registration not found for cancellation",
			zap.Uint("registration_id", uint(registrationID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusNotFound, "Registration not found")
		return
	}

	if user.Role != "admin" && registration.UserID != user.ID {
		h.logger.Warn("access denied to cancel registration",
			zap.Uint("user_id", user.ID),
			zap.Uint("registration_user_id", registration.UserID),
		)
		utils.RespondWithError(w, http.StatusForbidden, "Access denied")
		return
	}

	if err := h.registrationService.CancelRegistration(uint(registrationID)); err != nil {
		h.logger.Error("failed to cancel registration",
			zap.Uint("registration_id", uint(registrationID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to cancel registration: "+err.Error())
		return
	}

	h.logger.Info("registration cancelled successfully",
		zap.Uint("registration_id", uint(registrationID)),
		zap.Uint("user_id", user.ID),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]string{
		"message": "Registration cancelled successfully",
	})
}

// UpdateRegistrationStatus обновляет статус регистрации
func (h *EventRegistrationHandler) UpdateRegistrationStatus(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling update registration status request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Проверяем аутентификацию и права (только админы)
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("update registration status failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	if user.Role != "admin" {
		h.logger.Warn("update registration status failed - insufficient permissions",
			zap.Uint("user_id", user.ID),
			zap.String("user_role", user.Role),
		)
		utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
		return
	}

	// Извлекаем ID регистрации
	registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
	if err != nil {
		h.logger.Warn("invalid registration ID", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID")
		return
	}

	var req struct {
		Status string `json:"status" validate:"required,oneof=pending confirmed cancelled completed"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		h.logger.Error("failed to decode request body", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload")
		return
	}

	if err := utils.ValidateStruct(req); err != nil {
		h.logger.Warn("validation failed for update registration status", zap.Error(err))
		utils.RespondWithValidationError(w, err)
		return
	}

	if err := h.registrationService.UpdateRegistrationStatus(uint(registrationID), req.Status); err != nil {
		h.logger.Error("failed to update registration status",
			zap.Uint("registration_id", uint(registrationID)),
			zap.String("status", req.Status),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update registration status: "+err.Error())
		return
	}

	h.logger.Info("registration status updated successfully",
		zap.Uint("registration_id", uint(registrationID)),
		zap.String("status", req.Status),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]string{
		"message": "Registration status updated successfully",
	})
}

// UpdateResultTime обновляет результат забега
func (h *EventRegistrationHandler) UpdateResultTime(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling update result time request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Проверяем аутентификацию и права (только админы)
	user, ok := middleware.GetUserFromContext(r.Context())
	if !ok {
		h.logger.Warn("update result time failed - authentication required")
		utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
		return
	}

	if user.Role != "admin" {
		h.logger.Warn("update result time failed - insufficient permissions",
			zap.Uint("user_id", user.ID),
			zap.String("user_role", user.Role),
		)
		utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
		return
	}

	// Извлекаем ID регистрации
	registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
	if err != nil {
		h.logger.Warn("invalid registration ID", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID")
		return
	}

	var req struct {
		ResultTime string `json:"result_time" validate:"required,max=20"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		h.logger.Error("failed to decode request body", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload")
		return
	}

	if err := utils.ValidateStruct(req); err != nil {
		h.logger.Warn("validation failed for update result time", zap.Error(err))
		utils.RespondWithValidationError(w, err)
		return
	}

	if err := h.registrationService.UpdateResultTime(uint(registrationID), req.ResultTime); err != nil {
		h.logger.Error("failed to update result time",
			zap.Uint("registration_id", uint(registrationID)),
			zap.String("result_time", req.ResultTime),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update result time: "+err.Error())
		return
	}

	h.logger.Info("result time updated successfully",
		zap.Uint("registration_id", uint(registrationID)),
		zap.String("result_time", req.ResultTime),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]string{
		"message": "Result time updated successfully",
	})
}

// CheckEventAvailability проверяет доступность мест на событии
func (h *EventRegistrationHandler) CheckEventAvailability(w http.ResponseWriter, r *http.Request) {
	h.logger.Info("handling check event availability request",
		zap.String("method", r.Method),
		zap.String("path", r.URL.Path),
		zap.String("remote_addr", r.RemoteAddr),
	)

	// Извлекаем ID события
	eventID, err := strconv.ParseUint(r.PathValue("eventId"), 10, 32)
	if err != nil {
		h.logger.Warn("invalid event ID", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid event ID")
		return
	}

	available, err := h.registrationService.CheckEventAvailability(uint(eventID))
	if err != nil {
		h.logger.Error("failed to check event availability",
			zap.Uint("event_id", uint(eventID)),
			zap.Error(err),
		)
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to check availability: "+err.Error())
		return
	}

	h.logger.Info("event availability checked successfully",
		zap.Uint("event_id", uint(eventID)),
		zap.Bool("available", available),
	)

	utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
		"event_id":  eventID,
		"available": available,
	})
}

// toRegistrationResponse преобразует модель регистрации в response DTO
func toRegistrationResponse(registration *models.EventRegistration) RegistrationResponse {
	response := RegistrationResponse{
		ID:         registration.ID,
		UserID:     registration.UserID,
		EventID:    registration.EventID,
		Status:     registration.Status,
		Notes:      registration.Notes,
		ResultTime: registration.ResultTime,
		CreatedAt:  registration.CreatedAt.Format("2006-01-02 15:04:05"),
		UpdatedAt:  registration.UpdatedAt.Format("2006-01-02 15:04:05"),
	}

	// Включаем информацию о событии, если она загружена
	if registration.Event != nil {
		response.Event = toEventResponse(registration.Event)
	}

	return response
}