package ca

import (
	"testing"

	"github.com/uopi/goca/proto"
)

// ---- value coercion helpers ------------------------------------------

func TestToFloat64(t *testing.T) {
	cases := []struct {
		in   any
		want float64
	}{
		{float64(1.5), 1.5},
		{float32(2.5), 2.5},
		{int(3), 3},
		{int64(4), 4},
		{int32(5), 5},
		{int16(6), 6},
		{uint64(7), 7},
		{uint32(8), 8},
		{true, 1},
		{false, 0},
	}
	for _, c := range cases {
		got, err := toFloat64(c.in)
		if err != nil || got != c.want {
			t.Errorf("toFloat64(%v) = %v, %v; want %v", c.in, got, err, c.want)
		}
	}
	if _, err := toFloat64("nope"); err == nil {
		t.Error("toFloat64(string): want error")
	}
}

func TestToInt32(t *testing.T) {
	cases := []struct {
		in   any
		want int32
	}{
		{int(3), 3},
		{int64(4), 4},
		{int32(5), 5},
		{int16(6), 6},
		{float64(7.9), 7},
		{float32(8.9), 8},
		{true, 1},
		{false, 0},
	}
	for _, c := range cases {
		got, err := toInt32(c.in)
		if err != nil || got != c.want {
			t.Errorf("toInt32(%v) = %v, %v; want %v", c.in, got, err, c.want)
		}
	}
	if _, err := toInt32("nope"); err == nil {
		t.Error("toInt32(string): want error")
	}
}

func TestToString(t *testing.T) {
	if s, _ := toString("hi"); s != "hi" {
		t.Errorf("toString(string) = %q", s)
	}
	if s, _ := toString([]byte("bytes")); s != "bytes" {
		t.Errorf("toString([]byte) = %q", s)
	}
	if s, _ := toString(42); s != "42" {
		t.Errorf("toString(int) = %q, want 42", s)
	}
}

// ---- ConfigFromEnv ---------------------------------------------------

func TestConfigFromEnv(t *testing.T) {
	t.Setenv("EPICS_CA_ADDR_LIST", "1.2.3.4 5.6.7.8")
	t.Setenv("EPICS_CA_AUTO_ADDR_LIST", "no")
	cfg := ConfigFromEnv()
	if len(cfg.AddrList) != 2 || cfg.AddrList[0] != "1.2.3.4" {
		t.Errorf("AddrList = %v", cfg.AddrList)
	}
	if cfg.AutoAddrList {
		t.Error("AutoAddrList should be false when env = no")
	}
	if cfg.ClientName == "" {
		t.Error("ClientName should be populated")
	}

	t.Setenv("EPICS_CA_ADDR_LIST", "")
	t.Setenv("EPICS_CA_AUTO_ADDR_LIST", "yes")
	cfg = ConfigFromEnv()
	if !cfg.AutoAddrList {
		t.Error("AutoAddrList should default to true")
	}
	if len(cfg.AddrList) != 0 {
		t.Errorf("AddrList should be empty, got %v", cfg.AddrList)
	}
}

// ---- address helpers -------------------------------------------------

func TestResolveAddrs(t *testing.T) {
	got := resolveAddrs([]string{"host", "host2:9999", ""}, 5064)
	if len(got) != 2 || got[0] != "host:5064" || got[1] != "host2:9999" {
		t.Errorf("resolveAddrs = %v", got)
	}
}

func TestLocalBroadcastAddrs(t *testing.T) {
	// Should not panic; the result is environment-dependent.
	_ = localBroadcastAddrs(5064)
}

// ---- isAllFF ---------------------------------------------------------

func TestIsAllFF(t *testing.T) {
	if !isAllFF([]byte{0xFF, 0xFF, 0xFF}) {
		t.Error("all-FF should be true")
	}
	if isAllFF([]byte{0xFF, 0x00}) {
		t.Error("mixed should be false")
	}
}

// ---- EncodedSearchPair -----------------------------------------------

func TestEncodedSearchPair(t *testing.T) {
	pkt := EncodedSearchPair("TEST:PV", 7)
	// First header must be a VERSION message.
	h, _, err := proto.DecodeHeader(newBytesReader(pkt))
	if err != nil {
		t.Fatalf("DecodeHeader: %v", err)
	}
	if h.Command != proto.CmdVersion {
		t.Errorf("first command = %d, want CmdVersion", h.Command)
	}
}
