package health

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

func TestProbeHealthyAtBoot(t *testing.T) {
	p := New(time.Minute)
	if !p.Healthy() {
		t.Fatal("probe should be healthy immediately after creation")
	}
}

func TestProbeStaleAfterWindow(t *testing.T) {
	p := New(50 * time.Millisecond)
	time.Sleep(80 * time.Millisecond)
	if p.Healthy() {
		t.Fatal("probe should be stale after the staleness window elapses with no Bump")
	}
	p.Bump()
	if !p.Healthy() {
		t.Fatal("probe should recover immediately after Bump")
	}
}

func TestProbeHandlerStatusCodes(t *testing.T) {
	p := New(20 * time.Millisecond)

	// Fresh → 200
	if code := probeCode(p); code != http.StatusOK {
		t.Fatalf("fresh probe: want 200, got %d", code)
	}

	// Stale → 503
	time.Sleep(40 * time.Millisecond)
	if code := probeCode(p); code != http.StatusServiceUnavailable {
		t.Fatalf("stale probe: want 503, got %d", code)
	}
}

func TestProbeHandlerBody(t *testing.T) {
	p := New(time.Minute)
	rec := httptest.NewRecorder()
	p.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/healthz", nil))

	var body map[string]any
	if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
		t.Fatalf("invalid JSON body: %v (body=%q)", err, rec.Body.String())
	}
	if body["status"] != "ok" {
		t.Fatalf("want status=ok, got %v", body["status"])
	}
	if _, ok := body["last_heartbeat"].(string); !ok {
		t.Fatalf("want last_heartbeat string, got %v", body["last_heartbeat"])
	}
}

func TestNewDefaultsStale(t *testing.T) {
	p := New(0)
	if p.stale <= 0 {
		t.Fatal("New(0) should fall back to a positive staleness window")
	}
}

func probeCode(p *Probe) int {
	rec := httptest.NewRecorder()
	p.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/healthz", nil))
	return rec.Code
}
