// M13a W5: admin HTTP routes for deliverd-fcm. Two routes
// are registered when BA_AUTHD_JWT_SECRET is set:
//
//	GET  /v1/admin/dlq             — list DLQ rows for the
//	  `fcm` channel. Filters: company_id, alert_id, include=
//	  all. Read-only. Any authenticated user.
//	GET  /v1/admin/dlq/{id}        — fetch one row, including
//	  payload (for inspection before replay). Read-only.
//
// These mirror the global /v1/dlq endpoints in admind, but
// scoped to the FCM channel so a per-channel deliverd can
// answer "what's stuck in my DLQ" without needing access
// to admind.
//
// When BA_AUTHD_JWT_SECRET is unset, no admin routes are
// registered.
package main

import (
	"encoding/json"
	"log/slog"
	"net/http"
	"strconv"

	"git3.techno-world.net/lrosales/broad-announce/internal/authd"
	"git3.techno-world.net/lrosales/broad-announce/internal/dlq"
	"git3.techno-world.net/lrosales/broad-announce/internal/postgres"
)

// wireAdminRoutes wires the admin routes onto mux.
func wireAdminRoutes(mux *http.ServeMux, pool *postgres.Pool, logger *slog.Logger) {
	if !authd.EnvEnabled() {
		logger.Warn("admin routes are DISABLED (set BA_AUTHD_JWT_SECRET to enable)")
		return
	}
	ad, err := authd.NewFromEnv()
	if err != nil {
		logger.Error("BA_AUTHD_JWT_SECRET set but authd init failed; admin routes DISABLED", "err", err)
		return
	}
	logger.Info("admin routes enabled with JWT gate (fcm)")

	mux.Handle("GET /v1/admin/dlq",
		ad.RequireAuth(http.HandlerFunc(handleListDLQ(pool, logger))))
	mux.Handle("GET /v1/admin/dlq/{id}",
		ad.RequireAuth(http.HandlerFunc(handleGetDLQ(pool, logger))))
}

func handleListDLQ(pool *postgres.Pool, logger *slog.Logger) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
		offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
		rows, err := dlq.List(r.Context(), pool, dlq.ListFilters{
			Channel:          fcmChannel,
			CompanyID:        r.URL.Query().Get("company_id"),
			AlertID:          r.URL.Query().Get("alert_id"),
			IncludeDiscarded: r.URL.Query().Get("include") == "all",
			Limit:            limit,
			Offset:           offset,
		})
		if err != nil {
			logger.Error("dlq list (fcm)", "err", err)
			http.Error(w, "dlq list: "+err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{
			"channel": fcmChannel,
			"rows":    rows,
			"limit":   effectiveLimit(limit),
			"offset":  effectiveOffset(offset),
		})
	}
}

func handleGetDLQ(pool *postgres.Pool, logger *slog.Logger) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		raw := r.PathValue("id")
		id, err := strconv.ParseInt(raw, 10, 64)
		if err != nil || id <= 0 {
			http.Error(w, "bad id", http.StatusBadRequest)
			return
		}
		row, err := dlq.Get(r.Context(), pool, id)
		if err != nil {
			logger.Error("dlq get (fcm)", "err", err, "id", id)
			http.Error(w, "dlq get: "+err.Error(), http.StatusInternalServerError)
			return
		}
		if row == nil {
			http.Error(w, "not found", http.StatusNotFound)
			return
		}
		// Cross-channel safety: this deliverd-fcm binary only
		// knows about the fcm channel. If a row from another
		// channel ends up in the URL, treat it as not-found
		// rather than leaking data.
		if row.Channel != fcmChannel {
			http.Error(w, "not found", http.StatusNotFound)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(row)
	}
}

func effectiveLimit(n int) int {
	if n <= 0 || n > 500 {
		return 50
	}
	return n
}

func effectiveOffset(n int) int {
	if n < 0 {
		return 0
	}
	return n
}
