package metrics

import (
	"io"
	"net/http/httptest"
	"strings"
	"testing"
)

func scrape(t *testing.T, m *Metrics) string {
	t.Helper()
	rec := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/metrics", nil)
	m.Handler().ServeHTTP(rec, req)
	if rec.Code != 200 {
		t.Fatalf("metrics handler status = %d, want 200", rec.Code)
	}
	body, _ := io.ReadAll(rec.Result().Body)
	return string(body)
}

func TestNewRegistersAllCollectors(t *testing.T) {
	m := New(func() float64 { return 3 }, func() float64 { return 12.5 })
	// CounterVecs emit no series until a label set is observed; touch each once
	// so the metric family name surfaces in the scrape.
	m.RecordSessionExit("exited")
	m.RecordPermissionDecision("auto")
	m.RecordDeadLetter("webhook.deliver")
	body := scrape(t, m)

	for _, name := range []string{
		"acp_sessions_active",
		"acp_session_exit_total",
		"acp_permission_decisions_total",
		"jobs_dead_letter_total",
		"llm_cost_usd_total",
	} {
		if !strings.Contains(body, name) {
			t.Errorf("metrics output missing %q\n%s", name, body)
		}
	}
	// GaugeFunc values surface immediately even without any Record* call.
	if !strings.Contains(body, "acp_sessions_active 3") {
		t.Errorf("acp_sessions_active gauge not reporting func value; body:\n%s", body)
	}
	if !strings.Contains(body, "llm_cost_usd_total 12.5") {
		t.Errorf("llm_cost_usd_total gauge not reporting func value; body:\n%s", body)
	}
}

func TestRecordHelpersIncrementWithLabels(t *testing.T) {
	m := New(nil, nil)
	m.RecordSessionExit("crashed")
	m.RecordSessionExit("crashed")
	m.RecordSessionExit("exited")
	m.RecordPermissionDecision("approved")
	m.RecordPermissionDecision("denied")
	m.RecordPermissionDecision("expired")
	m.RecordDeadLetter("webhook.deliver")

	body := scrape(t, m)
	wants := []string{
		`acp_session_exit_total{status="crashed"} 2`,
		`acp_session_exit_total{status="exited"} 1`,
		`acp_permission_decisions_total{outcome="approved"} 1`,
		`acp_permission_decisions_total{outcome="denied"} 1`,
		`acp_permission_decisions_total{outcome="expired"} 1`,
		`jobs_dead_letter_total{type="webhook.deliver"} 1`,
	}
	for _, w := range wants {
		if !strings.Contains(body, w) {
			t.Errorf("metrics output missing %q\n%s", w, body)
		}
	}
}

func TestRegistryIsolatedNotGlobalDefault(t *testing.T) {
	// Two independent Metrics must not collide (would panic if both used the
	// global default registry).
	_ = New(nil, nil)
	_ = New(nil, nil)
}

func TestNilReceiverRecordSafe(t *testing.T) {
	var m *Metrics
	// Must not panic — callers may hold a nil *Metrics when metrics disabled.
	m.RecordSessionExit("exited")
	m.RecordPermissionDecision("auto")
	m.RecordDeadLetter("x")
}
