package commerce

import (
	"encoding/json"
	"errors"
	"log/slog"
	"net/http"
	"strconv"

	"github.com/go-chi/chi/v5"

	qmiddleware "qycw/go-server/internal/middleware"
	"qycw/go-server/internal/response"
)

type CartHandler struct {
	service *Service
	auth    *qmiddleware.Auth
	logger  *slog.Logger
}

func NewCartHandler(service *Service, auth *qmiddleware.Auth, logger *slog.Logger) *CartHandler {
	return &CartHandler{service: service, auth: auth, logger: logger}
}

func (h *CartHandler) Routes() http.Handler {
	r := chi.NewRouter()
	r.Use(h.auth.RequireUser)
	r.Get("/", h.listItems)
	r.Post("/", h.addItem)
	r.Patch("/{item_id}", h.updateItem)
	r.Delete("/{item_id}", h.deleteItem)
	r.Delete("/", h.clear)
	return r
}

func (h *CartHandler) listItems(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	data, err := h.service.ListCartItems(r.Context(), userID)
	h.writeResult(w, data, err)
}

func (h *CartHandler) addItem(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	var req CartItemCreateRequest
	if !decodeJSON(w, r, &req) {
		return
	}
	data, err := h.service.AddCartItem(r.Context(), userID, req)
	h.writeResult(w, data, err)
}

func (h *CartHandler) updateItem(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	itemID, ok := parseID(w, chi.URLParam(r, "item_id"))
	if !ok {
		return
	}
	var req CartItemUpdateRequest
	if !decodeJSON(w, r, &req) {
		return
	}
	data, err := h.service.UpdateCartItem(r.Context(), userID, itemID, req)
	h.writeResult(w, data, err)
}

func (h *CartHandler) deleteItem(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	itemID, ok := parseID(w, chi.URLParam(r, "item_id"))
	if !ok {
		return
	}
	err := h.service.DeleteCartItem(r.Context(), userID, itemID)
	h.writeResult(w, nil, err)
}

func (h *CartHandler) clear(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	data, err := h.service.ClearCart(r.Context(), userID)
	h.writeResult(w, data, err)
}

func (h *CartHandler) userID(w http.ResponseWriter, r *http.Request) (int64, bool) {
	principal, ok := qmiddleware.PrincipalFromContext(r.Context())
	if !ok || principal.ID <= 0 {
		response.Detail(w, http.StatusUnauthorized, "Could not validate credentials")
		return 0, false
	}
	return principal.ID, true
}

func (h *CartHandler) writeResult(w http.ResponseWriter, data any, err error) {
	if err != nil {
		h.writeError(w, err)
		return
	}
	response.OK(w, data)
}

func (h *CartHandler) writeError(w http.ResponseWriter, err error) {
	var httpErr HTTPError
	if errors.As(err, &httpErr) {
		response.Detail(w, httpErr.Status, httpErr.Detail)
		return
	}
	if h.logger != nil {
		h.logger.Error("cart handler error", "error", err)
	}
	response.Detail(w, http.StatusInternalServerError, msgServiceError)
}

func decodeJSON(w http.ResponseWriter, r *http.Request, out any) bool {
	defer r.Body.Close()
	if err := json.NewDecoder(r.Body).Decode(out); err != nil {
		response.Detail(w, http.StatusBadRequest, msgInvalidParams)
		return false
	}
	return true
}

func parseID(w http.ResponseWriter, value string) (int64, bool) {
	id, err := strconv.ParseInt(value, 10, 64)
	if err != nil || id <= 0 {
		response.Detail(w, http.StatusBadRequest, msgInvalidParams)
		return 0, false
	}
	return id, true
}
