package commerce

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

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

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

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

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

func (h *OrderHandler) Routes() http.Handler {
	r := chi.NewRouter()
	r.Use(h.auth.RequireUser)
	r.Post("/preview", h.previewCoupon)
	r.Post("/checkout", h.checkout)
	r.Get("/coupons/available", h.listAvailableCoupons)
	r.Post("/coupons/preview", h.previewCoupon)
	r.Get("/service-orders", h.listServiceOrders)
	r.Get("/", h.listOrders)
	r.Post("/{order_id}/unsubscribe", h.unsubscribeOrder)
	r.Get("/{order_id}", h.getOrder)
	return r
}

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

func (h *OrderHandler) unsubscribeOrder(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	orderID, ok := parseID(w, chi.URLParam(r, "order_id"))
	if !ok {
		return
	}
	data, err := h.service.UnsubscribeOrder(r.Context(), userID, orderID)
	h.writeResult(w, data, err)
}

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

func (h *OrderHandler) getOrder(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	orderID, ok := parseID(w, chi.URLParam(r, "order_id"))
	if !ok {
		return
	}
	data, err := h.service.GetOrder(r.Context(), userID, orderID)
	h.writeResult(w, data, err)
}

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

func (h *OrderHandler) previewCoupon(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	var req CouponPreviewRequest
	if !decodeOrderJSON(w, r, &req) {
		return
	}
	data, err := h.service.PreviewCoupon(r.Context(), userID, req)
	h.writeResult(w, data, err)
}

func (h *OrderHandler) checkout(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	var req CheckoutRequest
	if !decodeOrderJSON(w, r, &req) {
		return
	}
	data, err := h.service.Checkout(r.Context(), userID, req)
	h.writeResult(w, data, err)
}

func (h *OrderHandler) 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 *OrderHandler) writeResult(w http.ResponseWriter, data any, err error) {
	if err != nil {
		h.writeError(w, err)
		return
	}
	response.OK(w, data)
}

func (h *OrderHandler) 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("order handler error", "error", err)
	}
	response.Detail(w, http.StatusInternalServerError, msgServiceError)
}

func decodeOrderJSON(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
}
