package systemtables

import (
	"context"
	"net"
	"testing"

	"github.com/everything-on-mysql/cassandra-on-mysql/internal/config"
)

func TestUUIDV5KnownVector(t *testing.T) {
	got, err := uuidV5("6ba7b810-9dad-11d1-80b4-00c04fd430c8", "python.org")
	if err != nil {
		t.Fatalf("uuidV5 failed: %v", err)
	}
	const want = "886313e1-3b8a-5372-9b90-0c9aee199e5d"
	if got != want {
		t.Fatalf("uuidV5 mismatch: got %s want %s", got, want)
	}
}

func TestResolveLocalIPPriority(t *testing.T) {
	cfg := config.DefaultConfig()
	cfg.SystemTables.AdvertisedAddress = "10.0.0.10"
	cfg.Server.Listeners = []config.ServerListenerConfig{
		{Address: "192.168.0.1:9042", TLS: config.ServerTLSConfig{Mode: "disabled"}},
	}
	state, err := NewSystemTableState(cfg, nil)
	if err != nil {
		t.Fatalf("state init failed: %v", err)
	}

	ip, _, err := state.resolveLocalIP(context.Background())
	if err != nil {
		t.Fatalf("resolve local IP failed: %v", err)
	}
	if ip != "10.0.0.10" {
		t.Fatalf("expected advertised address, got %s", ip)
	}

	cfg.SystemTables.AdvertisedAddress = ""
	cfg.Server.Listeners = []config.ServerListenerConfig{
		{Address: "192.168.0.2:9042", TLS: config.ServerTLSConfig{Mode: "disabled"}},
	}
	state, err = NewSystemTableState(cfg, nil)
	if err != nil {
		t.Fatalf("state init failed: %v", err)
	}
	ip, _, err = state.resolveLocalIP(context.Background())
	if err != nil {
		t.Fatalf("resolve local IP failed: %v", err)
	}
	if ip != "192.168.0.2" {
		t.Fatalf("expected listen address IP, got %s", ip)
	}

	cfg.Server.Listeners = []config.ServerListenerConfig{
		{Address: "0.0.0.0:9042", TLS: config.ServerTLSConfig{Mode: "disabled"}},
	}
	state, err = NewSystemTableState(cfg, nil)
	if err != nil {
		t.Fatalf("state init failed: %v", err)
	}
	ctx := WithConnInfo(context.Background(), ConnInfo{
		LocalAddr: &net.TCPAddr{IP: net.ParseIP("10.0.0.9"), Port: 9042},
	})
	ip, _, err = state.resolveLocalIP(ctx)
	if err != nil {
		t.Fatalf("resolve local IP failed: %v", err)
	}
	if ip != "10.0.0.9" {
		t.Fatalf("expected connection local IP, got %s", ip)
	}

	ip, _, err = state.resolveLocalIP(context.Background())
	if err != nil {
		t.Fatalf("resolve local IP failed: %v", err)
	}
	if ip != "127.0.0.1" {
		t.Fatalf("expected fallback IP, got %s", ip)
	}
}

func TestResolveLocalPortPriority(t *testing.T) {
	cfg := config.DefaultConfig()
	cfg.SystemTables.NativePort = 9142
	cfg.Server.Listeners = []config.ServerListenerConfig{
		{Address: "127.0.0.1:9042", TLS: config.ServerTLSConfig{Mode: "disabled"}},
	}
	state, err := NewSystemTableState(cfg, nil)
	if err != nil {
		t.Fatalf("state init failed: %v", err)
	}
	if port := state.resolveLocalNativePort(context.Background()); port != 9142 {
		t.Fatalf("expected native port override, got %d", port)
	}

	cfg.SystemTables.NativePort = 0
	cfg.Server.Listeners = []config.ServerListenerConfig{
		{Address: "127.0.0.1:9043", TLS: config.ServerTLSConfig{Mode: "disabled"}},
	}
	state, err = NewSystemTableState(cfg, nil)
	if err != nil {
		t.Fatalf("state init failed: %v", err)
	}
	if port := state.resolveLocalNativePort(context.Background()); port != 9043 {
		t.Fatalf("expected listen port fallback, got %d", port)
	}

	cfg.Server.Listeners = []config.ServerListenerConfig{
		{Address: "127.0.0.1:0", TLS: config.ServerTLSConfig{Mode: "disabled"}},
	}
	state, err = NewSystemTableState(cfg, nil)
	if err != nil {
		t.Fatalf("state init failed: %v", err)
	}
	ctx := WithConnInfo(context.Background(), ConnInfo{
		LocalAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 9050},
	})
	if port := state.resolveLocalNativePort(ctx); port != 9050 {
		t.Fatalf("expected connection port fallback, got %d", port)
	}
}

func TestAutoTokenRing(t *testing.T) {
	ring, err := autoTokenRing(2)
	if err != nil {
		t.Fatalf("autoTokenRing failed: %v", err)
	}
	if len(ring) != 2 {
		t.Fatalf("expected ring size 2, got %d", len(ring))
	}
	if ring[0] != "-9223372036854775808" || ring[1] != "0" {
		t.Fatalf("unexpected ring tokens: %v", ring)
	}
}

func TestCapabilityProfileValidation(t *testing.T) {
	cfg := config.DefaultConfig()
	cfg.SystemTables.CapabilityProfile = "compat_v4"
	cfg.SystemTables.NativeProtocolVersion = "5"
	if _, err := NewSystemTableState(cfg, nil); err == nil {
		t.Fatalf("expected capability profile validation error")
	}
}

func TestHostIDDeterminism(t *testing.T) {
	ipHex, err := ip16Hex("127.0.0.1")
	if err != nil {
		t.Fatalf("ip16Hex failed: %v", err)
	}
	id1, err := hostIDFor(ipHex, 9042)
	if err != nil {
		t.Fatalf("hostIDFor failed: %v", err)
	}
	id2, err := hostIDFor(ipHex, 9042)
	if err != nil {
		t.Fatalf("hostIDFor failed: %v", err)
	}
	if id1 != id2 {
		t.Fatalf("expected stable host_id, got %s vs %s", id1, id2)
	}
	id3, err := hostIDFor(ipHex, 9043)
	if err != nil {
		t.Fatalf("hostIDFor failed: %v", err)
	}
	if id1 == id3 {
		t.Fatalf("expected host_id to change with port")
	}
}

func TestExplicitHostIDRemainsStableAcrossConnIdentity(t *testing.T) {
	cfg := config.DefaultConfig()
	cfg.SystemTables.HostID = "11111111-2222-4333-8444-555555555555"
	cfg.Server.Listeners = []config.ServerListenerConfig{
		{Address: "0.0.0.0:9042", TLS: config.ServerTLSConfig{Mode: "disabled"}},
	}
	state, err := NewSystemTableState(cfg, nil)
	if err != nil {
		t.Fatalf("state init failed: %v", err)
	}

	ctxA := WithConnInfo(context.Background(), ConnInfo{
		LocalAddr: &net.TCPAddr{IP: net.ParseIP("10.0.0.11"), Port: 9042},
	})
	ctxB := WithConnInfo(context.Background(), ConnInfo{
		LocalAddr: &net.TCPAddr{IP: net.ParseIP("10.0.0.12"), Port: 9142},
	})

	nodeA, err := state.localNode(ctxA)
	if err != nil {
		t.Fatalf("localNode A failed: %v", err)
	}
	nodeB, err := state.localNode(ctxB)
	if err != nil {
		t.Fatalf("localNode B failed: %v", err)
	}

	if nodeA.hostID != cfg.SystemTables.HostID {
		t.Fatalf("expected explicit host_id %s, got %s", cfg.SystemTables.HostID, nodeA.hostID)
	}
	if nodeB.hostID != cfg.SystemTables.HostID {
		t.Fatalf("expected explicit host_id %s, got %s", cfg.SystemTables.HostID, nodeB.hostID)
	}
}

func TestInvalidExplicitHostIDRejected(t *testing.T) {
	cfg := config.DefaultConfig()
	cfg.SystemTables.HostID = "not-a-uuid"
	if _, err := NewSystemTableState(cfg, nil); err == nil {
		t.Fatalf("expected invalid host_id error")
	}
}

func TestPeerOrdering(t *testing.T) {
	cfg := config.DefaultConfig()
	cfg.SystemTables.Peers = []config.SystemTablePeerCfg{
		{AdvertisedAddress: "10.0.0.2", NativePort: 9042},
		{AdvertisedAddress: "10.0.0.1", NativePort: 9043},
		{AdvertisedAddress: "10.0.0.1", NativePort: 9042},
	}
	state, err := NewSystemTableState(cfg, nil)
	if err != nil {
		t.Fatalf("state init failed: %v", err)
	}
	if len(state.peers) != 3 {
		t.Fatalf("expected 3 peers, got %d", len(state.peers))
	}
	if state.peers[0].address != "10.0.0.1" || state.peers[0].nativePort != 9042 {
		t.Fatalf("unexpected first peer: %+v", state.peers[0])
	}
	if state.peers[1].address != "10.0.0.1" || state.peers[1].nativePort != 9043 {
		t.Fatalf("unexpected second peer: %+v", state.peers[1])
	}
	if state.peers[2].address != "10.0.0.2" || state.peers[2].nativePort != 9042 {
		t.Fatalf("unexpected third peer: %+v", state.peers[2])
	}
}
