package infra

import (
	"encoding/json"
	"fmt"
)

// unibusHealth refleja la respuesta JSON del endpoint /healthz de un nodo del
// cluster de mensajería unibus (membershipd). Forma verificada en producción:
//
//	{"posture":{"enforce":true,"acl":true,"tls":true,"cluster":true,"store":"kv"},"status":"ok"}
type unibusHealth struct {
	Status  string `json:"status"`
	Posture struct {
		Enforce bool   `json:"enforce"`
		ACL     bool   `json:"acl"`
		TLS     bool   `json:"tls"`
		Cluster bool   `json:"cluster"`
		Store   string `json:"store"`
	} `json:"posture"`
}

// ParseUnibusHealth convierte la respuesta JSON del endpoint /healthz de un nodo
// del cluster de mensajería unibus en una serie de PromSample lista para empujar
// a VictoriaMetrics, sin instrumentar el bus (solo lee su endpoint de salud).
//
// node es el nombre lógico del nodo (p.ej. "magnus"); se adjunta a cada serie
// como las labels "node" e "instance" para distinguir los nodos cuando un único
// exporter scrapea varios. La función SOLO debe llamarse cuando el nodo
// respondió: el caso "no responde" (unibus_up=0) lo emite el llamador, no esta
// función, porque sin cuerpo no hay nada que parsear.
//
// Devuelve siete series por nodo:
//   - unibus_up = 1 (si el body parseó, el nodo respondió)
//   - unibus_status_ok = 1 si status=="ok", si no 0
//   - unibus_posture_enforce / _acl / _tls / _cluster = 1/0 según el booleano
//   - unibus_store_kv = 1 si posture.store=="kv", si no 0
//
// Si el body no es JSON válido con la forma esperada, devuelve (nil, error).
func ParseUnibusHealth(node string, body []byte) ([]PromSample, error) {
	var h unibusHealth
	if err := json.Unmarshal(body, &h); err != nil {
		return nil, fmt.Errorf("parse unibus healthz for node %q: %w", node, err)
	}
	b2f := func(b bool) float64 {
		if b {
			return 1
		}
		return 0
	}
	mk := func(name string, v float64) PromSample {
		return PromSample{
			Name:   name,
			Labels: map[string]string{"node": node, "instance": node},
			Value:  v,
		}
	}
	return []PromSample{
		mk("unibus_up", 1),
		mk("unibus_status_ok", b2f(h.Status == "ok")),
		mk("unibus_posture_enforce", b2f(h.Posture.Enforce)),
		mk("unibus_posture_acl", b2f(h.Posture.ACL)),
		mk("unibus_posture_tls", b2f(h.Posture.TLS)),
		mk("unibus_posture_cluster", b2f(h.Posture.Cluster)),
		mk("unibus_store_kv", b2f(h.Posture.Store == "kv")),
	}, nil
}
