package object

import (
	"encoding/json"
	"net/http"
	"strconv"

	"api_yal/internal/middleware"
	"github.com/go-chi/chi/v5"
)

type ObjectHandler struct {
	objectService ObjectService
}

func NewObjectHandler(objectService ObjectService) *ObjectHandler {
	return &ObjectHandler{
		objectService: objectService,
	}
}

// GetObjectByID обрабатывает GET /objects/{id}
func (h *ObjectHandler) GetObjectByID(w http.ResponseWriter, r *http.Request) {
	id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 32)
	if err != nil {
		http.Error(w, "Invalid object ID", http.StatusBadRequest)
		return
	}

	response, err := h.objectService.GetObjectByID(r.Context(), uint(id))
	if err != nil {
		h.handleError(w, err)
		return
	}

	h.respondWithJSON(w, http.StatusOK, response)
}

// CreateObject обрабатывает POST /objects
func (h *ObjectHandler) CreateObject(w http.ResponseWriter, r *http.Request) {
	var req CreateObjectRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	// Получаем ID пользователя из контекста (из AuthMiddleware)
	userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
	if !ok {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}
	
	// Устанавливаем owner_id из аутентифицированного пользователя
	req.OwnerID = userID

	response, err := h.objectService.CreateObject(r.Context(), &req)
	if err != nil {
		h.handleError(w, err)
		return
	}

	h.respondWithJSON(w, http.StatusCreated, response)
}

// UpdateObject обрабатывает PUT /objects/{id}
func (h *ObjectHandler) UpdateObject(w http.ResponseWriter, r *http.Request) {
	id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 32)
	if err != nil {
		http.Error(w, "Invalid object ID", http.StatusBadRequest)
		return
	}

	var req UpdateObjectRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	response, err := h.objectService.UpdateObject(r.Context(), uint(id), &req)
	if err != nil {
		h.handleError(w, err)
		return
	}

	h.respondWithJSON(w, http.StatusOK, response)
}

// DeleteObject обрабатывает DELETE /objects/{id}
func (h *ObjectHandler) DeleteObject(w http.ResponseWriter, r *http.Request) {
	id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 32)
	if err != nil {
		http.Error(w, "Invalid object ID", http.StatusBadRequest)
		return
	}

	if err := h.objectService.DeleteObject(r.Context(), uint(id)); err != nil {
		h.handleError(w, err)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

// ListObjects обрабатывает GET /objects
func (h *ObjectHandler) ListObjects(w http.ResponseWriter, r *http.Request) {
	req := &ListObjectsRequest{
		Page:     h.getQueryParamInt(r, "page", 1),
		PageSize: h.getQueryParamInt(r, "page_size", 10),
		Type:     r.URL.Query().Get("type"),
		Query:    r.URL.Query().Get("q"),
	}

	if statusStr := r.URL.Query().Get("is_active"); statusStr != "" {
		isActive, err := strconv.ParseBool(statusStr)
		if err == nil {
			req.Status = &isActive
		}
	}

	response, err := h.objectService.ListObjects(r.Context(), req)
	if err != nil {
		h.handleError(w, err)
		return
	}

	h.respondWithJSON(w, http.StatusOK, response)
}

// GetObjectsByOwner обрабатывает GET /objects/owner/{ownerId}
func (h *ObjectHandler) GetObjectsByOwner(w http.ResponseWriter, r *http.Request) {
	ownerID, err := strconv.ParseUint(chi.URLParam(r, "ownerId"), 10, 32)
	if err != nil {
		http.Error(w, "Invalid owner ID", http.StatusBadRequest)
		return
	}

	page := h.getQueryParamInt(r, "page", 1)
	pageSize := h.getQueryParamInt(r, "page_size", 10)

	response, err := h.objectService.GetObjectsByOwner(r.Context(), uint(ownerID), page, pageSize)
	if err != nil {
		h.handleError(w, err)
		return
	}

	h.respondWithJSON(w, http.StatusOK, response)
}

// SearchObjects обрабатывает GET /objects/search
func (h *ObjectHandler) SearchObjects(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query().Get("q")
	if query == "" {
		http.Error(w, "Search query is required", http.StatusBadRequest)
		return
	}

	page := h.getQueryParamInt(r, "page", 1)
	pageSize := h.getQueryParamInt(r, "page_size", 10)

	response, err := h.objectService.SearchObjects(r.Context(), query, page, pageSize)
	if err != nil {
		h.handleError(w, err)
		return
	}

	h.respondWithJSON(w, http.StatusOK, response)
}

// GetNearbyObjects обрабатывает GET /objects/nearby
func (h *ObjectHandler) GetNearbyObjects(w http.ResponseWriter, r *http.Request) {
	lat, err := strconv.ParseFloat(r.URL.Query().Get("lat"), 64)
	if err != nil {
		http.Error(w, "Invalid latitude", http.StatusBadRequest)
		return
	}

	lng, err := strconv.ParseFloat(r.URL.Query().Get("lng"), 64)
	if err != nil {
		http.Error(w, "Invalid longitude", http.StatusBadRequest)
		return
	}

	radius, err := strconv.ParseFloat(r.URL.Query().Get("radius"), 64)
	if err != nil || radius <= 0 {
		radius = 1000 // По умолчанию 1 км
	}

	page := h.getQueryParamInt(r, "page", 1)
	pageSize := h.getQueryParamInt(r, "page_size", 10)

	response, err := h.objectService.GetNearbyObjects(r.Context(), lat, lng, radius, page, pageSize)
	if err != nil {
		h.handleError(w, err)
		return
	}

	h.respondWithJSON(w, http.StatusOK, response)
}

// CreateFeedback обрабатывает POST /objects/{id}/feedbacks
func (h *ObjectHandler) CreateFeedback(w http.ResponseWriter, r *http.Request) {
	objectID, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 32)
	if err != nil {
		http.Error(w, "Invalid object ID", http.StatusBadRequest)
		return
	}

	var req CreateFeedbackRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	req.ObjectID = uint(objectID)

	userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
	if !ok {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	response, err := h.objectService.CreateFeedback(r.Context(), &req, userID)
	if err != nil {
		h.handleError(w, err)
		return
	}

	h.respondWithJSON(w, http.StatusCreated, response)
}

// CreateRatingVote обрабатывает POST /objects/{id}/ratings
func (h *ObjectHandler) CreateRatingVote(w http.ResponseWriter, r *http.Request) {
	objectID, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 32)
	if err != nil {
		http.Error(w, "Invalid object ID", http.StatusBadRequest)
		return
	}

	var req CreateRatingVoteRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	req.TargetID = uint(objectID)

	userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
	if !ok {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	response, err := h.objectService.CreateRatingVote(r.Context(), &req, userID)
	if err != nil {
		h.handleError(w, err)
		return
	}

	h.respondWithJSON(w, http.StatusCreated, response)
}

// Вспомогательные методы

func (h *ObjectHandler) respondWithJSON(w http.ResponseWriter, status int, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	if err := json.NewEncoder(w).Encode(data); err != nil {
		http.Error(w, "Failed to encode response", http.StatusInternalServerError)
	}
}

func (h *ObjectHandler) handleError(w http.ResponseWriter, err error) {
	switch err {
	case ErrObjectNotFound:
		http.Error(w, err.Error(), http.StatusNotFound)
	case ErrInvalidOwnerID, ErrShortNameRequired, ErrAlreadyVoted:
		http.Error(w, err.Error(), http.StatusBadRequest)
	case ErrNotImplemented:
		http.Error(w, err.Error(), http.StatusNotImplemented)
	default:
		http.Error(w, "Internal server error", http.StatusInternalServerError)
	}
}

func (h *ObjectHandler) getQueryParamInt(r *http.Request, key string, defaultValue int) int {
	value := r.URL.Query().Get(key)
	if value == "" {
		return defaultValue
	}
	intValue, err := strconv.Atoi(value)
	if err != nil {
		return defaultValue
	}
	return intValue
}