package handlers

// HTTP handlers for the per-user dashboard layout (t-paliad-219 Slice A2).
//
// Design: docs/design-dashboard-configurable-2026-05-20.md §9.
//
// Four endpoints:
//   GET  /api/me/dashboard-layout         → read (auto-seeds factory default)
//   PUT  /api/me/dashboard-layout         → replace (validates against catalog)
//   POST /api/me/dashboard-layout/reset   → overwrite with factory default
//   GET  /api/dashboard-widget-catalog    → catalog metadata for the picker

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

	"mgit.msbls.de/m/paliad/internal/services"
)

// GET /api/me/dashboard-layout — returns the caller's layout, seeding the
// factory default on first call. Always returns 200 with a valid
// DashboardLayoutSpec.
func handleGetDashboardLayout(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	if dbSvc.dashboardLayout == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "dashboard-layout service not configured"})
		return
	}
	spec, err := dbSvc.dashboardLayout.GetOrSeed(r.Context(), uid)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, spec)
}

// PUT /api/me/dashboard-layout — replaces the caller's layout. Body must
// be a complete DashboardLayoutSpec; the service validates against the
// catalog and 400s on a bad spec.
func handlePutDashboardLayout(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	if dbSvc.dashboardLayout == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "dashboard-layout service not configured"})
		return
	}
	var spec services.DashboardLayoutSpec
	if err := json.NewDecoder(r.Body).Decode(&spec); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON body"})
		return
	}
	out, err := dbSvc.dashboardLayout.Update(r.Context(), uid, spec)
	if err != nil {
		if errors.Is(err, services.ErrInvalidInput) {
			writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
			return
		}
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, out)
}

// POST /api/me/dashboard-layout/reset — overwrites the caller's layout
// with the factory default. The previous layout is discarded.
func handleResetDashboardLayout(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	if dbSvc.dashboardLayout == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "dashboard-layout service not configured"})
		return
	}
	spec, err := dbSvc.dashboardLayout.ResetToDefault(r.Context(), uid)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, spec)
}

// GET /api/dashboard-widget-catalog — returns the widget catalog. Auth-
// gated only because the catalog includes user-facing copy; nothing
// security-sensitive is exposed. The handler is DB-independent (the
// catalog is code-resident) so the requireDB gate is intentionally
// skipped — knowledge-platform-only deployments can still surface the
// catalog and we never want this endpoint to 503.
func handleGetWidgetCatalog(w http.ResponseWriter, r *http.Request) {
	if _, ok := requireUser(w, r); !ok {
		return
	}
	writeJSON(w, http.StatusOK, services.WidgetCatalog())
}
