package account

import (
	"errors"
	"fmt"
	"net/http"
	"strconv"

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

	"github.com/TaruLab/keluargamart-backend/internal/auth"
	"github.com/TaruLab/keluargamart-backend/pkg/httpresponse"
	"github.com/TaruLab/keluargamart-backend/pkg/logger"
	"github.com/TaruLab/keluargamart-backend/pkg/middleware"
)

type Handler struct {
	svc *Service
}

func NewHandler(svc *Service) *Handler {
	return &Handler{svc: svc}
}

// List returns the authenticated buyer's address book.
func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
	user := auth.UserFrom(r.Context())
	list, err := h.svc.List(r.Context(), user.ID)
	if err != nil {
		httpresponse.Internal(w, middleware.RequestIDFrom(r.Context()), logger.FromContext(r.Context()), err)
		return
	}
	httpresponse.OK(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), list)
}

// Create stores a new address for the authenticated buyer.
func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
	var in AddressInput
	if !decode(w, r, &in) {
		return
	}
	user := auth.UserFrom(r.Context())
	addr, err := h.svc.Create(r.Context(), user.ID, in)
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusCreated, middleware.RequestIDFrom(r.Context()), addr)
}

// Update replaces an address owned by the authenticated buyer.
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
	var in AddressInput
	if !decode(w, r, &in) {
		return
	}
	user := auth.UserFrom(r.Context())
	addr, err := h.svc.Update(r.Context(), user.ID, pathID(r), in)
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), addr)
}

// Delete removes an address owned by the authenticated buyer.
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
	user := auth.UserFrom(r.Context())
	if err := h.svc.Delete(r.Context(), user.ID, pathID(r)); err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), map[string]any{"deleted": true})
}

func respondErr(w http.ResponseWriter, r *http.Request, err error) {
	switch {
	case errors.Is(err, ErrNotFound):
		httpresponse.Error(w, http.StatusNotFound, middleware.RequestIDFrom(r.Context()), httpresponse.CodeNotFound, err.Error(), nil)
	case errors.Is(err, ErrInvalid):
		httpresponse.Error(w, http.StatusBadRequest, middleware.RequestIDFrom(r.Context()), httpresponse.CodeValidation, err.Error(), nil)
	default:
		httpresponse.Internal(w, middleware.RequestIDFrom(r.Context()), logger.FromContext(r.Context()), err)
	}
}

func decode(w http.ResponseWriter, r *http.Request, dst any) bool {
	return httpresponse.DecodeJSON(w, r, middleware.RequestIDFrom(r.Context()), dst)
}

func pathID(r *http.Request) int64 {
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	return id
}

// newInvalid wraps a validation message with the sentinel the handler maps to
// HTTP 400.
func newInvalid(msg string) error {
	return fmt.Errorf("%w: %s", ErrInvalid, msg)
}
