package handlers

// HTTP handlers for the firm-wide dashboard default layout (t-paliad-219
// Slice C). All four endpoints sit behind the adminGate so only
// global_admin can read or mutate. The per-user GetOrSeed/ResetToDefault
// path consumes the firm default via DashboardLayoutService — the read
// surface here is just the admin's read-back of the current row.
//
//   GET    /api/admin/firm-dashboard-default        — current row, or 204
//   PUT    /api/admin/firm-dashboard-default        — replace
//   DELETE /api/admin/firm-dashboard-default        — clear (revert to factory)
//   POST   /api/me/dashboard-layout/promote         — promote caller's own
//                                                     current layout to firm
//                                                     default. Admin convenience.

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

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

// GET /api/admin/firm-dashboard-default — returns the current firm-wide
// default layout, or 204 when none is set. Admins read this on the
// firm-default admin surface to verify the active layout.
func handleGetFirmDashboardDefault(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	if dbSvc.firmDashboardDefault == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "firm-dashboard-default service not configured"})
		return
	}
	spec, ok, err := dbSvc.firmDashboardDefault.Get(r.Context())
	if err != nil {
		writeServiceError(w, err)
		return
	}
	if !ok {
		// Empty firm default — the caller can fall back to the factory
		// shape via GET /api/dashboard-widget-catalog + FactoryDefault-
		// Layout logic mirrored client-side. 204 is cheaper than
		// shipping an "is_set: false" wrapper.
		w.WriteHeader(http.StatusNoContent)
		return
	}
	writeJSON(w, http.StatusOK, spec)
}

// PUT /api/admin/firm-dashboard-default — replace the firm-wide default.
// Body must be a complete DashboardLayoutSpec. The admin is recorded as
// updated_by for audit.
func handlePutFirmDashboardDefault(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	if dbSvc.firmDashboardDefault == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "firm-dashboard-default 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.firmDashboardDefault.Set(r.Context(), spec, uid)
	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)
}

// DELETE /api/admin/firm-dashboard-default — clear the firm default so
// future seeds/resets revert to the code-resident FactoryDefaultLayout.
// Idempotent.
func handleDeleteFirmDashboardDefault(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	if dbSvc.firmDashboardDefault == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "firm-dashboard-default service not configured"})
		return
	}
	if err := dbSvc.firmDashboardDefault.Clear(r.Context()); err != nil {
		writeServiceError(w, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

// POST /api/me/dashboard-layout/promote — admin convenience. Takes the
// caller's current layout (whatever's in user_dashboard_layouts for
// them) and promotes it to the firm-wide default. Saves an admin the
// step of crafting a layout in a JSON editor — they edit their own
// dashboard via the standard /dashboard editor, then promote one click.
//
// Admin-only at the route level (handlers.go wires this under adminGate).
// The handler itself does not re-check admin — that's the gate's job.
func handlePromoteDashboardLayoutToFirmDefault(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	if dbSvc.dashboardLayout == nil || dbSvc.firmDashboardDefault == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "dashboard-layout service not configured"})
		return
	}
	// Read the admin's own current layout (seeding the factory if they
	// somehow lack a row — vanishingly unlikely for an admin who's
	// logging in to promote, but the safety belt costs nothing).
	spec, err := dbSvc.dashboardLayout.GetOrSeed(r.Context(), uid)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	out, err := dbSvc.firmDashboardDefault.Set(r.Context(), spec, uid)
	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)
}
