package cli

import (
	"bytes"
	"context"
	"io"
	"net"
	"net/http"
	"strings"
	"testing"
	"time"
)

func TestMetricsCmdRegistered(t *testing.T) {
	found := false
	for _, c := range rootCmd.Commands() {
		if c.Name() == "metrics" {
			found = true
			break
		}
	}
	if !found {
		t.Fatal("metricsCmd not registered on root")
	}
}

func TestMetricsAddrFlagDefault(t *testing.T) {
	f := metricsCmd.Flags().Lookup("addr")
	if f == nil {
		t.Fatal("--addr flag not registered on metricsCmd")
	}
	if f.DefValue != ":9100" {
		t.Errorf("--addr default = %q, want %q", f.DefValue, ":9100")
	}
}

func TestMetricsEndpoints(t *testing.T) {
	_, cleanup := initTestEnv(t)
	defer cleanup()
	resetRootFlags(t)

	// Pick a free port by briefly listening then closing.
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("listen probe: %v", err)
	}
	addr := ln.Addr().String()
	_ = ln.Close()

	metricsAddr = addr

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	cmd := metricsCmd
	var out bytes.Buffer
	cmd.SetOut(&out)
	cmd.SetErr(&out)
	cmd.SetContext(ctx)

	errCh := make(chan error, 1)
	go func() {
		errCh <- cmd.RunE(cmd, nil)
	}()

	deadline := time.Now().Add(5 * time.Second)
	var resp *http.Response
	for time.Now().Before(deadline) {
		resp, err = http.Get("http://" + addr + "/healthz")
		if err == nil {
			break
		}
		time.Sleep(20 * time.Millisecond)
	}
	if err != nil {
		t.Fatalf("GET /healthz: %v", err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Errorf("/healthz status = %d, want 200", resp.StatusCode)
	}
	body, _ := io.ReadAll(resp.Body)
	resp.Body.Close()
	if !strings.HasPrefix(string(body), "ok") {
		t.Errorf("/healthz body = %q, want \"ok\"", string(body))
	}

	resp2, err := http.Get("http://" + addr + "/metrics")
	if err != nil {
		t.Fatalf("GET /metrics: %v", err)
	}
	defer resp2.Body.Close()
	if resp2.StatusCode != http.StatusOK {
		t.Errorf("/metrics status = %d, want 200", resp2.StatusCode)
	}
	mbody, _ := io.ReadAll(resp2.Body)
	ms := string(mbody)
	for _, name := range []string{
		"txns_applied_total",
		"txns_drifted_total",
		"drifts_remediated_total",
		"peers_total",
		"nodes_total",
		"allocs_total",
	} {
		if !strings.Contains(ms, name) {
			t.Errorf("/metrics missing %q\n---\n%s", name, ms)
		}
	}

	cancel()
	select {
	case <-errCh:
	case <-time.After(3 * time.Second):
		t.Fatal("metrics command did not stop after cancel")
	}
}
