package tickets

import (
	"errors"
	"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}
}

func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
	u := auth.UserFrom(r.Context())
	q := r.URL.Query()

	page := queryInt(q.Get("page"))
	if page < 1 {
		page = 1
	}
	perPage := queryInt(q.Get("per_page"))
	if perPage < 1 || perPage > 100 {
		perPage = 20
	}

	filter := ListTicketsFilter{
		Status:    q.Get("status"),
		Category:  q.Get("category"),
		Priority:  q.Get("priority"),
		SourceApp: q.Get("source_app"),
		Search:    q.Get("search"),
		Limit:     perPage,
		Offset:    (page - 1) * perPage,
	}

	tickets, total, err := h.svc.List(r.Context(), u, filter)
	if err != nil {
		respondErr(w, r, err)
		return
	}

	httpresponse.OKPaginated(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), tickets, page, perPage, int(total))
}

func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
	u := auth.UserFrom(r.Context())
	var req CreateTicketRequest
	if !decode(w, r, &req) {
		return
	}

	ticket, err := h.svc.Create(r.Context(), u, req)
	if err != nil {
		respondErr(w, r, err)
		return
	}

	httpresponse.OK(w, http.StatusCreated, middleware.RequestIDFrom(r.Context()), ticket)
}

func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
	u := auth.UserFrom(r.Context())
	id := pathID(r)
	if id <= 0 {
		httpresponse.Error(w, http.StatusBadRequest, middleware.RequestIDFrom(r.Context()), httpresponse.CodeValidation, "ID tiket tidak valid", nil)
		return
	}

	ticket, comments, err := h.svc.Get(r.Context(), u, id)
	if err != nil {
		respondErr(w, r, err)
		return
	}

	httpresponse.OK(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), map[string]any{
		"ticket":   ticket,
		"comments": comments,
	})
}

func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
	u := auth.UserFrom(r.Context())
	id := pathID(r)
	if id <= 0 {
		httpresponse.Error(w, http.StatusBadRequest, middleware.RequestIDFrom(r.Context()), httpresponse.CodeValidation, "ID tiket tidak valid", nil)
		return
	}

	var req UpdateTicketRequest
	if !decode(w, r, &req) {
		return
	}

	ticket, err := h.svc.Update(r.Context(), u, id, req)
	if err != nil {
		respondErr(w, r, err)
		return
	}

	httpresponse.OK(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), ticket)
}

func (h *Handler) AddComment(w http.ResponseWriter, r *http.Request) {
	u := auth.UserFrom(r.Context())
	id := pathID(r)
	if id <= 0 {
		httpresponse.Error(w, http.StatusBadRequest, middleware.RequestIDFrom(r.Context()), httpresponse.CodeValidation, "ID tiket tidak valid", nil)
		return
	}

	var req CreateCommentRequest
	if !decode(w, r, &req) {
		return
	}

	comment, err := h.svc.AddComment(r.Context(), u, id, req)
	if err != nil {
		respondErr(w, r, err)
		return
	}

	httpresponse.OK(w, http.StatusCreated, middleware.RequestIDFrom(r.Context()), comment)
}

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

func queryInt(s string) int {
	v, _ := strconv.Atoi(s)
	return v
}

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

func respondErr(w http.ResponseWriter, r *http.Request, err error) {
	rid := middleware.RequestIDFrom(r.Context())
	switch {
	case errors.Is(err, ErrTicketNotFound):
		httpresponse.Error(w, http.StatusNotFound, rid, httpresponse.CodeNotFound, err.Error(), nil)
	case errors.Is(err, ErrUnauthorized):
		httpresponse.Error(w, http.StatusForbidden, rid, httpresponse.CodeForbidden, err.Error(), nil)
	case errors.Is(err, ErrTitleRequired), errors.Is(err, ErrDescriptionRequired),
		errors.Is(err, ErrInvalidCategory), errors.Is(err, ErrInvalidPriority),
		errors.Is(err, ErrInvalidStatus), errors.Is(err, ErrCommentRequired):
		httpresponse.Error(w, http.StatusUnprocessableEntity, rid, httpresponse.CodeValidation, err.Error(), nil)
	case err != nil:
		httpresponse.SafeError(w, rid, logger.FromContext(r.Context()), http.StatusBadRequest, httpresponse.CodeValidation, err)
	}
}
