package salestracking

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"
)

// Handler binds the sales-tracking service to HTTP. Methods are exported so
// the router can attach RequirePermission per route in main.
type Handler struct {
	svc *Service
}

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

// --- visits ------------------------------------------------------------------

func (h *Handler) ListVisits(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	f := Filter{Page: queryInt(q.Get("page")), PerPage: queryInt(q.Get("per_page")),
		Status: q.Get("status"), DateFrom: q.Get("date_from"), DateTo: q.Get("date_to"), VisitType: q.Get("visit_type")}
	list, total, err := h.svc.ListVisits(r.Context(), f)
	if err != nil {
		httpresponse.Internal(w, middleware.RequestIDFrom(r.Context()), logger.FromContext(r.Context()), err)
		return
	}
	httpresponse.OKPaginated(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), list, norm(f.Page), norm(f.PerPage), total)
}

func (h *Handler) CreateVisit(w http.ResponseWriter, r *http.Request) {
	var in VisitInput
	if !decode(w, r, &in) {
		return
	}
	u := auth.UserFrom(r.Context())
	perm := SchedulePermission{
		CanBilling: u != nil && u.Can("sales_tracking.visits.create.billing"),
		CanRoutine: u != nil && u.Can("sales_tracking.visits.create.routine"),
	}
	v, err := h.svc.CreateVisit(r.Context(), userID(r), in, perm)
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusCreated, middleware.RequestIDFrom(r.Context()), v)
}

func (h *Handler) MyVisits(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	f := Filter{Page: queryInt(q.Get("page")), PerPage: queryInt(q.Get("per_page")),
		Status: q.Get("status"), DateFrom: q.Get("date_from"), DateTo: q.Get("date_to"), VisitType: q.Get("visit_type")}
	list, total, err := h.svc.MyVisits(r.Context(), userID(r), f)
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OKPaginated(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), list, norm(f.Page), norm(f.PerPage), total)
}

func (h *Handler) GetVisit(w http.ResponseWriter, r *http.Request) {
	v, err := h.svc.GetVisit(r.Context(), pathID(r))
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), v)
}

func (h *Handler) VisitCheckin(w http.ResponseWriter, r *http.Request) {
	var in GeoInput
	if !decode(w, r, &in) {
		return
	}
	u := auth.UserFrom(r.Context())
	isManager := u != nil && (u.Role == "admin" || u.Role == "superadmin" || u.Role == "gm" || u.Role == "spv" || u.Role == "bm" || u.Can("sales_tracking.visits.create") || u.Can("*"))
	v, err := h.svc.Checkin(r.Context(), userID(r), pathID(r), in, isManager)
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), v)
}

func (h *Handler) VisitCheckout(w http.ResponseWriter, r *http.Request) {
	var in GeoInput
	if !decode(w, r, &in) {
		return
	}
	u := auth.UserFrom(r.Context())
	isManager := u != nil && (u.Role == "admin" || u.Role == "superadmin" || u.Role == "gm" || u.Role == "spv" || u.Role == "bm" || u.Can("sales_tracking.visits.create") || u.Can("*"))
	v, err := h.svc.Checkout(r.Context(), userID(r), pathID(r), in, isManager)
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), v)
}

// --- activities --------------------------------------------------------------

func (h *Handler) ListActivities(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	f := Filter{Page: queryInt(q.Get("page")), PerPage: queryInt(q.Get("per_page")),
		Status: q.Get("status"), DateFrom: q.Get("date_from"), DateTo: q.Get("date_to"),
		Type:   q.Get("type")}
	list, total, err := h.svc.ListActivities(r.Context(), f)
	if err != nil {
		httpresponse.Internal(w, middleware.RequestIDFrom(r.Context()), logger.FromContext(r.Context()), err)
		return
	}
	httpresponse.OKPaginated(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), list, norm(f.Page), norm(f.PerPage), total)
}

func (h *Handler) CreateActivity(w http.ResponseWriter, r *http.Request) {
	var in ActivityInput
	if !decode(w, r, &in) {
		return
	}
	a, err := h.svc.CreateActivity(r.Context(), userID(r), in)
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusCreated, middleware.RequestIDFrom(r.Context()), a)
}

// --- routes ------------------------------------------------------------------

func (h *Handler) ListRoutes(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	f := Filter{Page: queryInt(q.Get("page")), PerPage: queryInt(q.Get("per_page")), Status: q.Get("status"),
		DateFrom: q.Get("date_from"), DateTo: q.Get("date_to")}
	list, total, err := h.svc.ListRoutes(r.Context(), f)
	if err != nil {
		httpresponse.Internal(w, middleware.RequestIDFrom(r.Context()), logger.FromContext(r.Context()), err)
		return
	}
	httpresponse.OKPaginated(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), list, norm(f.Page), norm(f.PerPage), total)
}

func (h *Handler) CreateRoute(w http.ResponseWriter, r *http.Request) {
	var in RouteInput
	if !decode(w, r, &in) {
		return
	}
	rt, err := h.svc.CreateRoute(r.Context(), userID(r), in)
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OK(w, http.StatusCreated, middleware.RequestIDFrom(r.Context()), rt)
}

func (h *Handler) MyRoutes(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	f := Filter{Page: queryInt(q.Get("page")), PerPage: queryInt(q.Get("per_page")), Status: q.Get("status")}
	list, total, err := h.svc.MyRoutes(r.Context(), userID(r), f)
	if err != nil {
		respondErr(w, r, err)
		return
	}
	httpresponse.OKPaginated(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), list, norm(f.Page), norm(f.PerPage), total)
}

// --- locations ---------------------------------------------------------------

func (h *Handler) PingLocations(w http.ResponseWriter, r *http.Request) {
	var in struct {
		Locations []LocationPingInput `json:"locations"`
	}
	if !decode(w, r, &in) {
		return
	}
	if len(in.Locations) == 0 {
		httpresponse.Error(w, http.StatusBadRequest, middleware.RequestIDFrom(r.Context()),
			httpresponse.CodeValidation, "locations is required", nil)
		return
	}
	if err := h.svc.PingLocations(r.Context(), userID(r), in.Locations); err != nil {
		respondErr(w, r, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

func (h *Handler) CurrentLocations(w http.ResponseWriter, r *http.Request) {
	list, err := h.svc.CurrentLocations(r.Context())
	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)
}

// ListSalesEmployees serves GET /sales-tracking/sales-employees — the
// jadwal kunjungan create form's sales-rep picker.
func (h *Handler) ListSalesEmployees(w http.ResponseWriter, r *http.Request) {
	list, err := h.svc.ListSalesEmployees(r.Context())
	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)
}

// ListCustomerLocations serves GET /sales-tracking/locations — the ERP
// tracking page's customer-site list (search/city filters + pagination).
func (h *Handler) ListCustomerLocations(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	f := Filter{Page: queryInt(q.Get("page")), PerPage: queryInt(q.Get("per_page")), Search: q.Get("search"), City: q.Get("city")}
	list, total, err := h.svc.ListCustomerLocations(r.Context(), f)
	if err != nil {
		httpresponse.Internal(w, middleware.RequestIDFrom(r.Context()), logger.FromContext(r.Context()), err)
		return
	}
	httpresponse.OKPaginated(w, http.StatusOK, middleware.RequestIDFrom(r.Context()), list, norm(f.Page), norm(f.PerPage), total)
}

// --- helpers -----------------------------------------------------------------

func userID(r *http.Request) int64 {
	u := auth.UserFrom(r.Context())
	if u == nil {
		return 0
	}
	return u.ID
}

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

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, ErrNotFound):
		httpresponse.Error(w, http.StatusNotFound, rid, httpresponse.CodeNotFound, "Not found", nil)
	case errors.Is(err, ErrNoEmployee):
		// A specific, actionable failure: the account has no employees row.
		// Folding this into 404 "Not found" made a visible button fail with a
		// message that pointed nowhere (2 Nov flow audit).
		httpresponse.Error(w, http.StatusUnprocessableEntity, rid, httpresponse.CodeValidation,
			"Akun ini belum terhubung ke data karyawan (employees) — hubungi HR untuk menautkannya", nil)
	case errors.Is(err, ErrCustomerReq):
		httpresponse.Error(w, http.StatusBadRequest, rid, httpresponse.CodeValidation, err.Error(), nil)
	case errors.Is(err, ErrVisitInvalid), errors.Is(err, ErrNotOwner):
		httpresponse.Error(w, http.StatusConflict, rid, httpresponse.CodeBusinessViolation, err.Error(), nil)
	case errors.Is(err, ErrScheduleForbidden):
		httpresponse.Error(w, http.StatusForbidden, rid, httpresponse.CodeForbidden, err.Error(), nil)
	case err != nil:
		httpresponse.SafeError(w, rid, logger.FromContext(r.Context()), http.StatusBadRequest, httpresponse.CodeValidation, err)
	}
}

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

func norm(n int) int {
	if n < 1 {
		return 20
	}
	return n
}
