package requisitions

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

	"github.com/TaruLab/keluargamart-backend/internal/auth"
	"github.com/TaruLab/keluargamart-backend/pkg/httpresponse"
	"github.com/TaruLab/keluargamart-backend/pkg/middleware"
	"github.com/go-chi/chi/v5"
)

// Handler serves the requisition routes.
type Handler struct {
	svc *Service
}

// NewHandler builds a Handler.
func NewHandler(svc *Service) *Handler { return &Handler{svc: svc} }

func respondErr(w http.ResponseWriter, r *http.Request, err error) {
	rid := middleware.RequestIDFrom(r.Context())
	switch {
	case errors.Is(err, ErrNotFound):
		httpresponse.Error(w, http.StatusNotFound, rid, httpresponse.CodeNotFound, err.Error(), nil)
	case errors.Is(err, ErrAlreadyReviewed):
		httpresponse.Error(w, http.StatusConflict, rid, httpresponse.CodeBusinessViolation, err.Error(), nil)
	case err != nil:
		// Validation and scope errors carry user-safe messages by construction.
		httpresponse.Error(w, http.StatusBadRequest, rid, httpresponse.CodeValidation, err.Error(), nil)
	}
}

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

// Create handles POST /purchase-requisitions — the store head files a
// request for items running out.
func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
	var in RequisitionInput
	if !httpresponse.DecodeJSON(w, r, middleware.RequestIDFrom(r.Context()), &in) {
		return
	}
	row, err := h.svc.Create(r.Context(), in)
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusCreated, middleware.RequestIDFrom(r.Context()), row)
}

// List handles GET /purchase-requisitions — the admin pembelian queue.
func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	page, per := 1, 20
	if v := intOrDefault(q.Get("page")); v > 0 {
		page = v
	}
	if v := intOrDefault(q.Get("per_page")); v > 0 {
		per = v
	}
	rows, total, err := h.svc.List(r.Context(), ListFilter{
		Page:        page,
		PerPage:     per,
		Status:      q.Get("status"),
		StoreID:     int64OrZero(q.Get("store_id")),
		RequestedBy: int64OrZero(q.Get("requested_by")),
		SortBy:      q.Get("sort_by"),
		SortDir:     q.Get("sort_dir"),
	})
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OKPaginated(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), rows, page, per, int(total))
}

// Get handles GET /purchase-requisitions/{id} — header + item lines.
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
	row, items, err := h.svc.Get(r.Context(), pathID(r))
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), map[string]any{
		"requisition": row,
		"items":       items,
	})
}

// Review handles POST /purchase-requisitions/{id}/review — admin pembelian
// approves (naming the created PO) or rejects the request.
func (h *Handler) Review(w http.ResponseWriter, r *http.Request) {
	var in ReviewInput
	if !httpresponse.DecodeJSON(w, r, middleware.RequestIDFrom(r.Context()), &in) {
		return
	}
	reviewerID := int64(0)
	if u := auth.UserFrom(r.Context()); u != nil {
		reviewerID = u.ID
	}
	row, err := h.svc.Review(r.Context(), pathID(r), in, reviewerID)
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), row)
}

func intOrDefault(s string) int {
	n, _ := strconv.Atoi(s)
	if n < 1 {
		return 1
	}
	return n
}

func perPageOrDefault(n int) int {
	if n < 1 || n > 100 {
		return 20
	}
	return n
}

func int64OrZero(s string) int64 {
	n, _ := strconv.ParseInt(s, 10, 64)
	return n
}
