package templates

import (
	"encoding/json"
	"errors"
	"net/http"
	"strconv"
	"strings"

	"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 serves HTTP endpoints for report template management.
type Handler struct {
	svc *Service
}

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

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

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

// ListDefinitions serves GET /reports/definitions
func (h *Handler) ListDefinitions(w http.ResponseWriter, r *http.Request) {
	rid := middleware.RequestIDFrom(r.Context())
	defs := h.svc.ListDefinitions()
	httpresponse.OK(w, http.StatusOK, rid, defs)
}

// GetDefinition serves GET /reports/definitions/{code}
func (h *Handler) GetDefinition(w http.ResponseWriter, r *http.Request) {
	rid := middleware.RequestIDFrom(r.Context())
	code := chi.URLParam(r, "code")
	def, err := h.svc.GetDefinition(code)
	if err != nil {
		httpresponse.Error(w, http.StatusNotFound, rid, httpresponse.CodeNotFound, "Report definition not found", nil)
		return
	}
	httpresponse.OK(w, http.StatusOK, rid, def)
}

// ListTemplates serves GET /reports/templates
func (h *Handler) ListTemplates(w http.ResponseWriter, r *http.Request) {
	rid := middleware.RequestIDFrom(r.Context())
	q := r.URL.Query()
	reportCode := strings.TrimSpace(q.Get("report_code"))
	status := strings.TrimSpace(q.Get("status"))

	items, err := h.svc.ListTemplates(r.Context(), reportCode, status)
	if err != nil {
		httpresponse.SafeError(w, rid, logger.FromContext(r.Context()), http.StatusInternalServerError, httpresponse.CodeInternal, err)
		return
	}
	if items == nil {
		items = make([]ReportTemplate, 0)
	}
	httpresponse.OK(w, http.StatusOK, rid, items)
}

// GetTemplate serves GET /reports/templates/{id}
func (h *Handler) GetTemplate(w http.ResponseWriter, r *http.Request) {
	rid := middleware.RequestIDFrom(r.Context())
	id := pathID(r)
	if id <= 0 {
		httpresponse.Error(w, http.StatusBadRequest, rid, httpresponse.CodeValidation, "Invalid template ID", nil)
		return
	}

	tpl, err := h.svc.GetTemplate(r.Context(), id)
	if err != nil {
		if errors.Is(err, ErrNotFound) {
			httpresponse.Error(w, http.StatusNotFound, rid, httpresponse.CodeNotFound, "Template not found", nil)
			return
		}
		httpresponse.SafeError(w, rid, logger.FromContext(r.Context()), http.StatusInternalServerError, httpresponse.CodeInternal, err)
		return
	}
	httpresponse.OK(w, http.StatusOK, rid, tpl)
}

// CreateTemplate serves POST /reports/templates
func (h *Handler) CreateTemplate(w http.ResponseWriter, r *http.Request) {
	rid := middleware.RequestIDFrom(r.Context())
	var in CreateTemplateInput
	if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
		httpresponse.Error(w, http.StatusBadRequest, rid, httpresponse.CodeValidation, "Invalid request body", nil)
		return
	}

	tpl, err := h.svc.CreateTemplate(r.Context(), in, currentUserID(r))
	if err != nil {
		httpresponse.SafeError(w, rid, logger.FromContext(r.Context()), http.StatusBadRequest, httpresponse.CodeValidation, err)
		return
	}
	httpresponse.OK(w, http.StatusCreated, rid, tpl)
}

// UpdateTemplate serves PUT /reports/templates/{id}
func (h *Handler) UpdateTemplate(w http.ResponseWriter, r *http.Request) {
	rid := middleware.RequestIDFrom(r.Context())
	id := pathID(r)
	if id <= 0 {
		httpresponse.Error(w, http.StatusBadRequest, rid, httpresponse.CodeValidation, "Invalid template ID", nil)
		return
	}

	var in UpdateTemplateInput
	if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
		httpresponse.Error(w, http.StatusBadRequest, rid, httpresponse.CodeValidation, "Invalid request body", nil)
		return
	}

	tpl, err := h.svc.UpdateTemplate(r.Context(), id, in, currentUserID(r))
	if err != nil {
		if errors.Is(err, ErrNotFound) {
			httpresponse.Error(w, http.StatusNotFound, rid, httpresponse.CodeNotFound, "Template not found", nil)
			return
		}
		if errors.Is(err, ErrCannotModify) {
			httpresponse.Error(w, http.StatusConflict, rid, httpresponse.CodeConflict, "Archived template cannot be modified", nil)
			return
		}
		httpresponse.SafeError(w, rid, logger.FromContext(r.Context()), http.StatusInternalServerError, httpresponse.CodeInternal, err)
		return
	}
	httpresponse.OK(w, http.StatusOK, rid, tpl)
}

// ActivateTemplate serves POST /reports/templates/{id}/activate
func (h *Handler) ActivateTemplate(w http.ResponseWriter, r *http.Request) {
	rid := middleware.RequestIDFrom(r.Context())
	id := pathID(r)
	if id <= 0 {
		httpresponse.Error(w, http.StatusBadRequest, rid, httpresponse.CodeValidation, "Invalid template ID", nil)
		return
	}

	err := h.svc.ActivateTemplate(r.Context(), id, currentUserID(r))
	if err != nil {
		if errors.Is(err, ErrNotFound) {
			httpresponse.Error(w, http.StatusNotFound, rid, httpresponse.CodeNotFound, "Template not found", nil)
			return
		}
		httpresponse.SafeError(w, rid, logger.FromContext(r.Context()), http.StatusInternalServerError, httpresponse.CodeInternal, err)
		return
	}
	httpresponse.OK(w, http.StatusOK, rid, map[string]any{"activated": true, "id": id})
}

// DuplicateTemplate serves POST /reports/templates/{id}/duplicate
func (h *Handler) DuplicateTemplate(w http.ResponseWriter, r *http.Request) {
	rid := middleware.RequestIDFrom(r.Context())
	id := pathID(r)
	if id <= 0 {
		httpresponse.Error(w, http.StatusBadRequest, rid, httpresponse.CodeValidation, "Invalid template ID", nil)
		return
	}

	tpl, err := h.svc.DuplicateTemplate(r.Context(), id, currentUserID(r))
	if err != nil {
		if errors.Is(err, ErrNotFound) {
			httpresponse.Error(w, http.StatusNotFound, rid, httpresponse.CodeNotFound, "Template not found", nil)
			return
		}
		httpresponse.SafeError(w, rid, logger.FromContext(r.Context()), http.StatusInternalServerError, httpresponse.CodeInternal, err)
		return
	}
	httpresponse.OK(w, http.StatusCreated, rid, tpl)
}
