package local

import (
	"context"
	"fmt"
	"net"
	"net/netip"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/miekg/dns"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/netbirdio/netbird/client/internal/dns/test"
	nbdns "github.com/netbirdio/netbird/dns"
)

// mockResolver implements resolver for testing
type mockResolver struct {
	lookupFunc func(ctx context.Context, network, host string) ([]netip.Addr, error)
}

func (m *mockResolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {
	if m.lookupFunc != nil {
		return m.lookupFunc(ctx, network, host)
	}
	return nil, nil
}

// mockPeerConnectivity returns canned (known, connected) results per IP.
// Used by the disconnected-peer filter tests below. IPs not in the map
// are reported as unknown so the filter leaves them alone.
type mockPeerConnectivity struct {
	byIP map[string]struct{ known, connected bool }
}

func (m mockPeerConnectivity) IsConnectedByIP(ip netip.Addr) (known, connected bool) {
	v, ok := m.byIP[ip.String()]
	if !ok {
		return false, false
	}
	return v.known, v.connected
}

func TestLocalResolver_ServeDNS(t *testing.T) {
	recordA := nbdns.SimpleRecord{
		Name:  "peera.netbird.cloud.",
		Type:  1,
		Class: nbdns.DefaultClass,
		TTL:   300,
		RData: "1.2.3.4",
	}

	recordCNAME := nbdns.SimpleRecord{
		Name:  "peerb.netbird.cloud.",
		Type:  5,
		Class: nbdns.DefaultClass,
		TTL:   300,
		RData: "www.netbird.io",
	}

	wild := "wild.netbird.cloud."

	recordWild := nbdns.SimpleRecord{
		Name:  "*." + wild,
		Type:  1,
		Class: nbdns.DefaultClass,
		TTL:   300,
		RData: "1.2.3.4",
	}

	specificRecord := nbdns.SimpleRecord{
		Name:  "existing." + wild,
		Type:  1,
		Class: nbdns.DefaultClass,
		TTL:   300,
		RData: "5.6.7.8",
	}

	testCases := []struct {
		name                string
		inputRecord         nbdns.SimpleRecord
		inputMSG            *dns.Msg
		responseShouldBeNil bool
	}{
		{
			name:        "Should Resolve A Record",
			inputRecord: recordA,
			inputMSG:    new(dns.Msg).SetQuestion(recordA.Name, dns.TypeA),
		},
		{
			name:        "Should Resolve CNAME Record",
			inputRecord: recordCNAME,
			inputMSG:    new(dns.Msg).SetQuestion(recordCNAME.Name, dns.TypeCNAME),
		},
		{
			name:                "Should Not Write When Not Found A Record",
			inputRecord:         recordA,
			inputMSG:            new(dns.Msg).SetQuestion("not.found.com", dns.TypeA),
			responseShouldBeNil: true,
		},
		{
			name:        "Should Resolve A Wild Record",
			inputRecord: recordWild,
			inputMSG:    new(dns.Msg).SetQuestion("test."+wild, dns.TypeA),
		},
		{
			name:        "Should Resolve A more specific Record",
			inputRecord: specificRecord,
			inputMSG:    new(dns.Msg).SetQuestion(specificRecord.Name, dns.TypeA),
		},
	}

	for _, testCase := range testCases {
		t.Run(testCase.name, func(t *testing.T) {
			resolver := NewResolver()
			_ = resolver.RegisterRecord(testCase.inputRecord)
			_ = resolver.RegisterRecord(recordWild)
			var responseMSG *dns.Msg
			responseWriter := &test.MockResponseWriter{
				WriteMsgFunc: func(m *dns.Msg) error {
					responseMSG = m
					return nil
				},
			}

			resolver.ServeDNS(responseWriter, testCase.inputMSG)

			if responseMSG == nil || len(responseMSG.Answer) == 0 {
				if testCase.responseShouldBeNil {
					return
				}
				t.Fatalf("should write a response message")
			}

			answerString := responseMSG.Answer[0].String()
			if !strings.Contains(answerString, testCase.inputMSG.Question[0].Name) {
				t.Fatalf("answer doesn't contain the same domain name: \nWant: %s\nGot:%s", testCase.name, answerString)
			}
			if !strings.Contains(answerString, dns.Type(testCase.inputRecord.Type).String()) {
				t.Fatalf("answer doesn't contain the correct type: \nWant: %s\nGot:%s", dns.Type(testCase.inputRecord.Type).String(), answerString)
			}
			if !strings.Contains(answerString, testCase.inputRecord.RData) {
				t.Fatalf("answer doesn't contain the same address: \nWant: %s\nGot:%s", testCase.inputRecord.RData, answerString)
			}
		})
	}
}

// TestLocalResolver_Update_StaleRecord verifies that updating
// a record correctly replaces the old one, preventing stale entries.
func TestLocalResolver_Update_StaleRecord(t *testing.T) {
	recordName := "host.example.com."
	recordType := dns.TypeA
	recordClass := dns.ClassINET

	record1 := nbdns.SimpleRecord{
		Name: recordName, Type: int(recordType), Class: nbdns.DefaultClass, TTL: 300, RData: "1.1.1.1",
	}
	record2 := nbdns.SimpleRecord{
		Name: recordName, Type: int(recordType), Class: nbdns.DefaultClass, TTL: 300, RData: "2.2.2.2",
	}

	recordKey := dns.Question{Name: recordName, Qtype: uint16(recordClass), Qclass: recordType}

	resolver := NewResolver()

	zone1 := []nbdns.CustomZone{{Domain: "example.com.", Records: []nbdns.SimpleRecord{record1}}}
	zone2 := []nbdns.CustomZone{{Domain: "example.com.", Records: []nbdns.SimpleRecord{record2}}}

	// Apply first update
	resolver.Update(zone1)

	// Verify first update
	resolver.mu.RLock()
	rrSlice1, found1 := resolver.records[recordKey]
	resolver.mu.RUnlock()

	require.True(t, found1, "Record key %s not found after first update", recordKey)
	require.Len(t, rrSlice1, 1, "Should have exactly 1 record after first update")
	assert.Contains(t, rrSlice1[0].String(), record1.RData, "Record after first update should be %s", record1.RData)

	// Apply second update
	resolver.Update(zone2)

	// Verify second update
	resolver.mu.RLock()
	rrSlice2, found2 := resolver.records[recordKey]
	resolver.mu.RUnlock()

	require.True(t, found2, "Record key %s not found after second update", recordKey)
	require.Len(t, rrSlice2, 1, "Should have exactly 1 record after update overwriting the key")
	assert.Contains(t, rrSlice2[0].String(), record2.RData, "The single record should be the updated one (%s)", record2.RData)
	assert.NotContains(t, rrSlice2[0].String(), record1.RData, "The stale record (%s) should not be present", record1.RData)
}

// TestLocalResolver_MultipleRecords_SameQuestion verifies that multiple records
// with the same question are stored properly
func TestLocalResolver_MultipleRecords_SameQuestion(t *testing.T) {
	resolver := NewResolver()

	recordName := "multi.example.com."
	recordType := dns.TypeA

	// Create two records with the same name and type but different IPs
	record1 := nbdns.SimpleRecord{
		Name: recordName, Type: int(recordType), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1",
	}
	record2 := nbdns.SimpleRecord{
		Name: recordName, Type: int(recordType), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.2",
	}

	zones := []nbdns.CustomZone{{Domain: "example.com.", Records: []nbdns.SimpleRecord{record1, record2}}}

	// Apply update with both records
	resolver.Update(zones)

	// Create question that matches both records
	question := dns.Question{
		Name:   recordName,
		Qtype:  recordType,
		Qclass: dns.ClassINET,
	}

	// Verify both records are stored
	resolver.mu.RLock()
	records, found := resolver.records[question]
	resolver.mu.RUnlock()

	require.True(t, found, "Records for question %v not found", question)
	require.Len(t, records, 2, "Should have exactly 2 records for the same question")

	// Verify both record data values are present
	recordStrings := []string{records[0].String(), records[1].String()}
	assert.Contains(t, recordStrings[0]+recordStrings[1], record1.RData, "First record data should be present")
	assert.Contains(t, recordStrings[0]+recordStrings[1], record2.RData, "Second record data should be present")
}

// TestLocalResolver_RecordRotation verifies that records are rotated in a round-robin fashion
func TestLocalResolver_RecordRotation(t *testing.T) {
	resolver := NewResolver()

	recordName := "rotation.example.com."
	recordType := dns.TypeA

	// Create three records with the same name and type but different IPs
	record1 := nbdns.SimpleRecord{
		Name: recordName, Type: int(recordType), Class: nbdns.DefaultClass, TTL: 300, RData: "192.168.1.1",
	}
	record2 := nbdns.SimpleRecord{
		Name: recordName, Type: int(recordType), Class: nbdns.DefaultClass, TTL: 300, RData: "192.168.1.2",
	}
	record3 := nbdns.SimpleRecord{
		Name: recordName, Type: int(recordType), Class: nbdns.DefaultClass, TTL: 300, RData: "192.168.1.3",
	}

	zones := []nbdns.CustomZone{{Domain: "example.com.", Records: []nbdns.SimpleRecord{record1, record2, record3}}}

	// Apply update with all three records
	resolver.Update(zones)

	msg := new(dns.Msg).SetQuestion(recordName, recordType)

	// First lookup - should return the records in original order
	var responses [3]*dns.Msg

	// Perform three lookups to verify rotation
	for i := 0; i < 3; i++ {
		responseWriter := &test.MockResponseWriter{
			WriteMsgFunc: func(m *dns.Msg) error {
				responses[i] = m
				return nil
			},
		}

		resolver.ServeDNS(responseWriter, msg)
	}

	// Verify all three responses contain answers
	for i, resp := range responses {
		require.NotNil(t, resp, "Response %d should not be nil", i)
		require.Len(t, resp.Answer, 3, "Response %d should have 3 answers", i)
	}

	// Verify the first record in each response is different due to rotation
	firstRecordIPs := []string{
		responses[0].Answer[0].String(),
		responses[1].Answer[0].String(),
		responses[2].Answer[0].String(),
	}

	// Each record should be different (rotated)
	assert.NotEqual(t, firstRecordIPs[0], firstRecordIPs[1], "First lookup should differ from second lookup due to rotation")
	assert.NotEqual(t, firstRecordIPs[1], firstRecordIPs[2], "Second lookup should differ from third lookup due to rotation")
	assert.NotEqual(t, firstRecordIPs[0], firstRecordIPs[2], "First lookup should differ from third lookup due to rotation")

	// After three rotations, we should have cycled through all records
	assert.Contains(t, firstRecordIPs[0]+firstRecordIPs[1]+firstRecordIPs[2], record1.RData)
	assert.Contains(t, firstRecordIPs[0]+firstRecordIPs[1]+firstRecordIPs[2], record2.RData)
	assert.Contains(t, firstRecordIPs[0]+firstRecordIPs[1]+firstRecordIPs[2], record3.RData)
}

// TestLocalResolver_CaseInsensitiveMatching verifies that DNS record lookups are case-insensitive
func TestLocalResolver_CaseInsensitiveMatching(t *testing.T) {
	resolver := NewResolver()

	// Create record with lowercase name
	lowerCaseRecord := nbdns.SimpleRecord{
		Name:  "lower.example.com.",
		Type:  int(dns.TypeA),
		Class: nbdns.DefaultClass,
		TTL:   300,
		RData: "10.10.10.10",
	}

	// Create record with mixed case name
	mixedCaseRecord := nbdns.SimpleRecord{
		Name:  "MiXeD.ExAmPlE.CoM.",
		Type:  int(dns.TypeA),
		Class: nbdns.DefaultClass,
		TTL:   300,
		RData: "20.20.20.20",
	}

	// Update resolver with the records
	resolver.Update([]nbdns.CustomZone{{Domain: "example.com.", Records: []nbdns.SimpleRecord{lowerCaseRecord, mixedCaseRecord}}})

	testCases := []struct {
		name          string
		queryName     string
		expectedRData string
		shouldResolve bool
	}{
		{
			name:          "Query lowercase with lowercase record",
			queryName:     "lower.example.com.",
			expectedRData: "10.10.10.10",
			shouldResolve: true,
		},
		{
			name:          "Query uppercase with lowercase record",
			queryName:     "LOWER.EXAMPLE.COM.",
			expectedRData: "10.10.10.10",
			shouldResolve: true,
		},
		{
			name:          "Query mixed case with lowercase record",
			queryName:     "LoWeR.eXaMpLe.CoM.",
			expectedRData: "10.10.10.10",
			shouldResolve: true,
		},
		{
			name:          "Query lowercase with mixed case record",
			queryName:     "mixed.example.com.",
			expectedRData: "20.20.20.20",
			shouldResolve: true,
		},
		{
			name:          "Query uppercase with mixed case record",
			queryName:     "MIXED.EXAMPLE.COM.",
			expectedRData: "20.20.20.20",
			shouldResolve: true,
		},
		{
			name:          "Query with different casing pattern",
			queryName:     "mIxEd.ExaMpLe.cOm.",
			expectedRData: "20.20.20.20",
			shouldResolve: true,
		},
		{
			name:          "Query non-existent domain",
			queryName:     "nonexistent.example.com.",
			shouldResolve: false,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			var responseMSG *dns.Msg

			// Create DNS query with the test case name
			msg := new(dns.Msg).SetQuestion(tc.queryName, dns.TypeA)

			// Create mock response writer to capture the response
			responseWriter := &test.MockResponseWriter{
				WriteMsgFunc: func(m *dns.Msg) error {
					responseMSG = m
					return nil
				},
			}

			// Perform DNS query
			resolver.ServeDNS(responseWriter, msg)

			// Check if we expect a successful resolution
			if !tc.shouldResolve {
				if responseMSG == nil || len(responseMSG.Answer) == 0 {
					// Expected no answer, test passes
					return
				}
				t.Fatalf("Expected no resolution for %s, but got answer: %v", tc.queryName, responseMSG.Answer)
			}

			// Verify we got a response
			require.NotNil(t, responseMSG, "Should have received a response message")
			require.Greater(t, len(responseMSG.Answer), 0, "Response should contain at least one answer")

			// Verify the response contains the expected data
			answerString := responseMSG.Answer[0].String()
			assert.Contains(t, answerString, tc.expectedRData,
				"Answer should contain the expected IP address %s, got: %s",
				tc.expectedRData, answerString)
		})
	}
}

// TestLocalResolver_CNAMEFallback verifies that the resolver correctly falls back
// to checking for CNAME records when the requested record type isn't found
func TestLocalResolver_CNAMEFallback(t *testing.T) {
	resolver := NewResolver()

	// Create a CNAME record (but no A record for this name)
	cnameRecord := nbdns.SimpleRecord{
		Name:  "alias.example.com.",
		Type:  int(dns.TypeCNAME),
		Class: nbdns.DefaultClass,
		TTL:   300,
		RData: "target.example.com.",
	}

	// Create an A record for the CNAME target
	targetRecord := nbdns.SimpleRecord{
		Name:  "target.example.com.",
		Type:  int(dns.TypeA),
		Class: nbdns.DefaultClass,
		TTL:   300,
		RData: "192.168.100.100",
	}

	// Update resolver with both records
	resolver.Update([]nbdns.CustomZone{{Domain: "example.com.", Records: []nbdns.SimpleRecord{cnameRecord, targetRecord}}})

	testCases := []struct {
		name          string
		queryName     string
		queryType     uint16
		expectedType  string
		expectedRData string
		shouldResolve bool
	}{
		{
			name:          "Directly query CNAME record",
			queryName:     "alias.example.com.",
			queryType:     dns.TypeCNAME,
			expectedType:  "CNAME",
			expectedRData: "target.example.com.",
			shouldResolve: true,
		},
		{
			name:          "Query A record but get CNAME fallback",
			queryName:     "alias.example.com.",
			queryType:     dns.TypeA,
			expectedType:  "CNAME",
			expectedRData: "target.example.com.",
			shouldResolve: true,
		},
		{
			name:          "Query AAAA record but get CNAME fallback",
			queryName:     "alias.example.com.",
			queryType:     dns.TypeAAAA,
			expectedType:  "CNAME",
			expectedRData: "target.example.com.",
			shouldResolve: true,
		},
		{
			name:          "Query direct A record",
			queryName:     "target.example.com.",
			queryType:     dns.TypeA,
			expectedType:  "A",
			expectedRData: "192.168.100.100",
			shouldResolve: true,
		},
		{
			name:          "Query non-existent name",
			queryName:     "nonexistent.example.com.",
			queryType:     dns.TypeA,
			shouldResolve: false,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			var responseMSG *dns.Msg

			// Create DNS query with the test case parameters
			msg := new(dns.Msg).SetQuestion(tc.queryName, tc.queryType)

			// Create mock response writer to capture the response
			responseWriter := &test.MockResponseWriter{
				WriteMsgFunc: func(m *dns.Msg) error {
					responseMSG = m
					return nil
				},
			}

			// Perform DNS query
			resolver.ServeDNS(responseWriter, msg)

			// Check if we expect a successful resolution
			if !tc.shouldResolve {
				if responseMSG == nil || len(responseMSG.Answer) == 0 || responseMSG.Rcode != dns.RcodeSuccess {
					// Expected no resolution, test passes
					return
				}
				t.Fatalf("Expected no resolution for %s, but got answer: %v", tc.queryName, responseMSG.Answer)
			}

			// Verify we got a successful response
			require.NotNil(t, responseMSG, "Should have received a response message")
			require.Equal(t, dns.RcodeSuccess, responseMSG.Rcode, "Response should have success status code")
			require.Greater(t, len(responseMSG.Answer), 0, "Response should contain at least one answer")

			// Verify the response contains the expected data
			answerString := responseMSG.Answer[0].String()
			assert.Contains(t, answerString, tc.expectedType,
				"Answer should be of type %s, got: %s", tc.expectedType, answerString)
			assert.Contains(t, answerString, tc.expectedRData,
				"Answer should contain the expected data %s, got: %s", tc.expectedRData, answerString)
		})
	}
}

// TestLocalResolver_NoErrorWithDifferentRecordType verifies that querying for a record type
// that doesn't exist but where other record types exist for the same domain returns NOERROR
// with 0 records instead of NXDOMAIN
func TestLocalResolver_NoErrorWithDifferentRecordType(t *testing.T) {
	resolver := NewResolver()
	// Mock external resolver for CNAME target resolution
	resolver.resolver = &mockResolver{
		lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
			if host == "target.example.com." {
				if network == "ip4" {
					return []netip.Addr{netip.MustParseAddr("93.184.216.34")}, nil
				}
				if network == "ip6" {
					return []netip.Addr{netip.MustParseAddr("2606:2800:220:1:248:1893:25c8:1946")}, nil
				}
			}
			return nil, &net.DNSError{IsNotFound: true, Name: host}
		},
	}

	recordA := nbdns.SimpleRecord{
		Name:  "example.netbird.cloud.",
		Type:  int(dns.TypeA),
		Class: nbdns.DefaultClass,
		TTL:   300,
		RData: "192.168.1.100",
	}

	recordCNAME := nbdns.SimpleRecord{
		Name:  "alias.netbird.cloud.",
		Type:  int(dns.TypeCNAME),
		Class: nbdns.DefaultClass,
		TTL:   300,
		RData: "target.example.com.",
	}

	resolver.Update([]nbdns.CustomZone{{Domain: "netbird.cloud.", Records: []nbdns.SimpleRecord{recordA, recordCNAME}}})

	testCases := []struct {
		name           string
		queryName      string
		queryType      uint16
		expectedRcode  int
		shouldHaveData bool
	}{
		{
			name:           "Query A record that exists",
			queryName:      "example.netbird.cloud.",
			queryType:      dns.TypeA,
			expectedRcode:  dns.RcodeSuccess,
			shouldHaveData: true,
		},
		{
			name:           "Query AAAA for domain with only A record",
			queryName:      "example.netbird.cloud.",
			queryType:      dns.TypeAAAA,
			expectedRcode:  dns.RcodeSuccess,
			shouldHaveData: false,
		},
		{
			name:           "Query other record with different case and non-fqdn",
			queryName:      "EXAMPLE.netbird.cloud",
			queryType:      dns.TypeAAAA,
			expectedRcode:  dns.RcodeSuccess,
			shouldHaveData: false,
		},
		{
			name:           "Query TXT for domain with only A record",
			queryName:      "example.netbird.cloud.",
			queryType:      dns.TypeTXT,
			expectedRcode:  dns.RcodeSuccess,
			shouldHaveData: false,
		},
		{
			name:           "Query A for domain with only CNAME record",
			queryName:      "alias.netbird.cloud.",
			queryType:      dns.TypeA,
			expectedRcode:  dns.RcodeSuccess,
			shouldHaveData: true,
		},
		{
			name:           "Query AAAA for domain with only CNAME record",
			queryName:      "alias.netbird.cloud.",
			queryType:      dns.TypeAAAA,
			expectedRcode:  dns.RcodeSuccess,
			shouldHaveData: true,
		},
		{
			name:           "Query for completely non-existent domain",
			queryName:      "nonexistent.netbird.cloud.",
			queryType:      dns.TypeA,
			expectedRcode:  dns.RcodeNameError,
			shouldHaveData: false,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			var responseMSG *dns.Msg

			msg := new(dns.Msg).SetQuestion(tc.queryName, tc.queryType)

			responseWriter := &test.MockResponseWriter{
				WriteMsgFunc: func(m *dns.Msg) error {
					responseMSG = m
					return nil
				},
			}

			resolver.ServeDNS(responseWriter, msg)

			require.NotNil(t, responseMSG, "Should have received a response message")

			assert.Equal(t, tc.expectedRcode, responseMSG.Rcode,
				"Response code should be %d (%s)",
				tc.expectedRcode, dns.RcodeToString[tc.expectedRcode])

			if tc.shouldHaveData {
				assert.Greater(t, len(responseMSG.Answer), 0, "Response should contain answers")
			} else {
				assert.Equal(t, 0, len(responseMSG.Answer), "Response should contain no answers")
			}
		})
	}
}

// TestLocalResolver_CNAMEChainResolution tests comprehensive CNAME chain following
func TestLocalResolver_CNAMEChainResolution(t *testing.T) {
	t.Run("simple internal CNAME chain", func(t *testing.T) {
		resolver := NewResolver()
		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "192.168.1.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 2)

		cname, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "target.example.com.", cname.Target)

		a, ok := resp.Answer[1].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "192.168.1.1", a.A.String())
	})

	t.Run("multi-hop CNAME chain", func(t *testing.T) {
		resolver := NewResolver()
		resolver.Update([]nbdns.CustomZone{{
			Domain: "test.",
			Records: []nbdns.SimpleRecord{
				{Name: "hop1.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "hop2.test."},
				{Name: "hop2.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "hop3.test."},
				{Name: "hop3.test.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("hop1.test.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 3)
	})

	t.Run("CNAME to non-existent internal target returns only CNAME", func(t *testing.T) {
		resolver := NewResolver()
		resolver.Update([]nbdns.CustomZone{{
			Domain: "test.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "nonexistent.test."},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.test.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Len(t, resp.Answer, 1)
		_, ok := resp.Answer[0].(*dns.CNAME)
		assert.True(t, ok)
	})
}

// TestLocalResolver_CNAMEMaxDepth tests the maximum depth limit for CNAME chains
func TestLocalResolver_CNAMEMaxDepth(t *testing.T) {
	t.Run("chain at max depth resolves", func(t *testing.T) {
		resolver := NewResolver()
		var records []nbdns.SimpleRecord
		// Create chain of 7 CNAMEs (under max of 8)
		for i := 1; i <= 7; i++ {
			records = append(records, nbdns.SimpleRecord{
				Name:  fmt.Sprintf("hop%d.test.", i),
				Type:  int(dns.TypeCNAME),
				Class: nbdns.DefaultClass,
				TTL:   300,
				RData: fmt.Sprintf("hop%d.test.", i+1),
			})
		}
		records = append(records, nbdns.SimpleRecord{
			Name: "hop8.test.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.10.10.10",
		})

		resolver.Update([]nbdns.CustomZone{{Domain: "test.", Records: records}})

		msg := new(dns.Msg).SetQuestion("hop1.test.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 8)
	})

	t.Run("chain exceeding max depth stops", func(t *testing.T) {
		resolver := NewResolver()
		var records []nbdns.SimpleRecord
		// Create chain of 10 CNAMEs (exceeds max of 8)
		for i := 1; i <= 10; i++ {
			records = append(records, nbdns.SimpleRecord{
				Name:  fmt.Sprintf("deep%d.test.", i),
				Type:  int(dns.TypeCNAME),
				Class: nbdns.DefaultClass,
				TTL:   300,
				RData: fmt.Sprintf("deep%d.test.", i+1),
			})
		}
		records = append(records, nbdns.SimpleRecord{
			Name: "deep11.test.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.10.10.10",
		})

		resolver.Update([]nbdns.CustomZone{{Domain: "test.", Records: records}})

		msg := new(dns.Msg).SetQuestion("deep1.test.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		// Should NOT have the final A record (chain too deep)
		assert.LessOrEqual(t, len(resp.Answer), 8)
	})

	t.Run("circular CNAME is protected by max depth", func(t *testing.T) {
		resolver := NewResolver()
		resolver.Update([]nbdns.CustomZone{{
			Domain: "test.",
			Records: []nbdns.SimpleRecord{
				{Name: "loop1.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "loop2.test."},
				{Name: "loop2.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "loop1.test."},
			},
		}})

		msg := new(dns.Msg).SetQuestion("loop1.test.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.LessOrEqual(t, len(resp.Answer), 8)
	})
}

// TestLocalResolver_ExternalCNAMEResolution tests CNAME resolution to external domains
func TestLocalResolver_ExternalCNAMEResolution(t *testing.T) {
	t.Run("CNAME to external domain resolves via external resolver", func(t *testing.T) {
		resolver := NewResolver()
		resolver.resolver = &mockResolver{
			lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
				if host == "external.example.com." && network == "ip4" {
					return []netip.Addr{netip.MustParseAddr("93.184.216.34")}, nil
				}
				return nil, nil
			},
		}

		resolver.Update([]nbdns.CustomZone{{
			Domain: "test.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "external.example.com."},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.test.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Len(t, resp.Answer, 2, "Should have CNAME + A record")

		cname, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "external.example.com.", cname.Target)

		a, ok := resp.Answer[1].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "93.184.216.34", a.A.String())
	})

	t.Run("CNAME to external domain resolves IPv6", func(t *testing.T) {
		resolver := NewResolver()
		resolver.resolver = &mockResolver{
			lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
				if host == "external.example.com." && network == "ip6" {
					return []netip.Addr{netip.MustParseAddr("2606:2800:220:1:248:1893:25c8:1946")}, nil
				}
				return nil, nil
			},
		}

		resolver.Update([]nbdns.CustomZone{{
			Domain: "test.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "external.example.com."},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.test.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Len(t, resp.Answer, 2, "Should have CNAME + AAAA record")

		cname, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "external.example.com.", cname.Target)

		aaaa, ok := resp.Answer[1].(*dns.AAAA)
		require.True(t, ok)
		assert.Equal(t, "2606:2800:220:1:248:1893:25c8:1946", aaaa.AAAA.String())
	})

	t.Run("concurrent external resolution", func(t *testing.T) {
		resolver := NewResolver()
		resolver.resolver = &mockResolver{
			lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
				if host == "external.example.com." && network == "ip4" {
					return []netip.Addr{netip.MustParseAddr("93.184.216.34")}, nil
				}
				return nil, nil
			},
		}

		resolver.Update([]nbdns.CustomZone{{
			Domain: "test.",
			Records: []nbdns.SimpleRecord{
				{Name: "concurrent.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "external.example.com."},
			},
		}})

		var wg sync.WaitGroup
		results := make([]*dns.Msg, 10)

		for i := 0; i < 10; i++ {
			wg.Add(1)
			go func(idx int) {
				defer wg.Done()
				msg := new(dns.Msg).SetQuestion("concurrent.test.", dns.TypeA)
				var resp *dns.Msg
				resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)
				results[idx] = resp
			}(i)
		}
		wg.Wait()

		for i, resp := range results {
			require.NotNil(t, resp, "Response %d should not be nil", i)
			require.Len(t, resp.Answer, 2, "Response %d should have CNAME + A", i)
		}
	})
}

// TestLocalResolver_ZoneManagement tests zone-aware CNAME resolution
func TestLocalResolver_ZoneManagement(t *testing.T) {
	t.Run("Update sets zones correctly", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{
			{Domain: "example.com.", Records: []nbdns.SimpleRecord{
				{Name: "host.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			}},
			{Domain: "test.local."},
		})

		assert.True(t, resolver.isInManagedZone("host.example.com."))
		assert.True(t, resolver.isInManagedZone("other.example.com."))
		assert.True(t, resolver.isInManagedZone("sub.test.local."))
		assert.False(t, resolver.isInManagedZone("external.com."))
	})

	t.Run("isInManagedZone case insensitive", func(t *testing.T) {
		resolver := NewResolver()
		resolver.Update([]nbdns.CustomZone{{Domain: "Example.COM."}})

		assert.True(t, resolver.isInManagedZone("host.example.com."))
		assert.True(t, resolver.isInManagedZone("HOST.EXAMPLE.COM."))
	})

	t.Run("Update clears zones", func(t *testing.T) {
		resolver := NewResolver()
		resolver.Update([]nbdns.CustomZone{{Domain: "example.com."}})
		assert.True(t, resolver.isInManagedZone("host.example.com."))

		resolver.Update(nil)
		assert.False(t, resolver.isInManagedZone("host.example.com."))
	})
}

// TestLocalResolver_CNAMEZoneAwareResolution tests CNAME resolution with zone awareness
func TestLocalResolver_CNAMEZoneAwareResolution(t *testing.T) {
	t.Run("CNAME target in managed zone returns NXDOMAIN per RFC 6604", func(t *testing.T) {
		resolver := NewResolver()
		resolver.Update([]nbdns.CustomZone{{
			Domain: "myzone.test.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.myzone.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "nonexistent.myzone.test."},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.myzone.test.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeNameError, resp.Rcode, "Should return NXDOMAIN")
		require.Len(t, resp.Answer, 1, "Should include CNAME in answer")
	})

	t.Run("CNAME to external domain skips zone check", func(t *testing.T) {
		resolver := NewResolver()
		resolver.resolver = &mockResolver{
			lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
				if host == "external.other.com." && network == "ip4" {
					return []netip.Addr{netip.MustParseAddr("203.0.113.1")}, nil
				}
				return nil, nil
			},
		}

		resolver.Update([]nbdns.CustomZone{{
			Domain: "myzone.test.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.myzone.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "external.other.com."},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.myzone.test.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 2, "Should have CNAME + A from external resolution")
	})

	t.Run("CNAME target exists with different type returns NODATA not NXDOMAIN", func(t *testing.T) {
		resolver := NewResolver()
		// CNAME points to target that has A but no AAAA - query for AAAA should be NODATA
		resolver.Update([]nbdns.CustomZone{{
			Domain: "myzone.test.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.myzone.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.myzone.test."},
				{Name: "target.myzone.test.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "1.1.1.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.myzone.test.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should return NODATA (success), not NXDOMAIN")
		require.Len(t, resp.Answer, 1, "Should have only CNAME, no AAAA")
		_, ok := resp.Answer[0].(*dns.CNAME)
		assert.True(t, ok, "Answer should be CNAME record")
	})

	t.Run("external CNAME target exists but no AAAA records (NODATA)", func(t *testing.T) {
		resolver := NewResolver()
		resolver.resolver = &mockResolver{
			lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
				if host == "external.example.com." {
					if network == "ip6" {
						// No AAAA records
						return nil, &net.DNSError{IsNotFound: true, Name: host}
					}
					if network == "ip4" {
						// But A records exist - domain exists
						return []netip.Addr{netip.MustParseAddr("93.184.216.34")}, nil
					}
				}
				return nil, &net.DNSError{IsNotFound: true, Name: host}
			},
		}

		resolver.Update([]nbdns.CustomZone{{
			Domain: "test.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "external.example.com."},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.test.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should return NODATA (success), not NXDOMAIN")
		require.Len(t, resp.Answer, 1, "Should have only CNAME")
		_, ok := resp.Answer[0].(*dns.CNAME)
		assert.True(t, ok, "Answer should be CNAME record")
	})

	// Table-driven test for all external resolution outcomes
	externalCases := []struct {
		name           string
		lookupFunc     func(context.Context, string, string) ([]netip.Addr, error)
		expectedRcode  int
		expectedAnswer int
	}{
		{
			name: "external NXDOMAIN (both A and AAAA not found)",
			lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
				return nil, &net.DNSError{IsNotFound: true, Name: host}
			},
			expectedRcode:  dns.RcodeNameError,
			expectedAnswer: 1, // CNAME only
		},
		{
			name: "external SERVFAIL (temporary error)",
			lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
				return nil, &net.DNSError{IsTemporary: true, Name: host}
			},
			expectedRcode:  dns.RcodeServerFailure,
			expectedAnswer: 1, // CNAME only
		},
		{
			name: "external SERVFAIL (timeout)",
			lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
				return nil, &net.DNSError{IsTimeout: true, Name: host}
			},
			expectedRcode:  dns.RcodeServerFailure,
			expectedAnswer: 1, // CNAME only
		},
		{
			name: "external SERVFAIL (generic error)",
			lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
				return nil, fmt.Errorf("connection refused")
			},
			expectedRcode:  dns.RcodeServerFailure,
			expectedAnswer: 1, // CNAME only
		},
		{
			name: "external success with IPs",
			lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
				if network == "ip4" {
					return []netip.Addr{netip.MustParseAddr("93.184.216.34")}, nil
				}
				return nil, &net.DNSError{IsNotFound: true, Name: host}
			},
			expectedRcode:  dns.RcodeSuccess,
			expectedAnswer: 2, // CNAME + A
		},
	}

	for _, tc := range externalCases {
		t.Run(tc.name, func(t *testing.T) {
			resolver := NewResolver()
			resolver.resolver = &mockResolver{lookupFunc: tc.lookupFunc}

			resolver.Update([]nbdns.CustomZone{{
				Domain: "test.",
				Records: []nbdns.SimpleRecord{
					{Name: "alias.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "external.example.com."},
				},
			}})

			msg := new(dns.Msg).SetQuestion("alias.test.", dns.TypeA)
			var resp *dns.Msg
			resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

			require.NotNil(t, resp)
			assert.Equal(t, tc.expectedRcode, resp.Rcode, "rcode mismatch")
			assert.Len(t, resp.Answer, tc.expectedAnswer, "answer count mismatch")
			if tc.expectedAnswer > 0 {
				_, ok := resp.Answer[0].(*dns.CNAME)
				assert.True(t, ok, "first answer should be CNAME")
			}
		})
	}
}

// TestLocalResolver_Fallthrough verifies that non-authoritative zones
// trigger fallthrough (Zero bit set) when no records match
func TestLocalResolver_Fallthrough(t *testing.T) {
	resolver := NewResolver()

	record := nbdns.SimpleRecord{
		Name:  "existing.custom.zone.",
		Type:  int(dns.TypeA),
		Class: nbdns.DefaultClass,
		TTL:   300,
		RData: "10.0.0.1",
	}

	testCases := []struct {
		name              string
		zones             []nbdns.CustomZone
		queryName         string
		expectFallthrough bool
		expectRecord      bool
	}{
		{
			name: "Authoritative zone returns NXDOMAIN without fallthrough",
			zones: []nbdns.CustomZone{{
				Domain:  "custom.zone.",
				Records: []nbdns.SimpleRecord{record},
			}},
			queryName:         "nonexistent.custom.zone.",
			expectFallthrough: false,
			expectRecord:      false,
		},
		{
			name: "Non-authoritative zone triggers fallthrough",
			zones: []nbdns.CustomZone{{
				Domain:           "custom.zone.",
				Records:          []nbdns.SimpleRecord{record},
				NonAuthoritative: true,
			}},
			queryName:         "nonexistent.custom.zone.",
			expectFallthrough: true,
			expectRecord:      false,
		},
		{
			name: "Record found in non-authoritative zone returns normally",
			zones: []nbdns.CustomZone{{
				Domain:           "custom.zone.",
				Records:          []nbdns.SimpleRecord{record},
				NonAuthoritative: true,
			}},
			queryName:         "existing.custom.zone.",
			expectFallthrough: false,
			expectRecord:      true,
		},
		{
			name: "Record found in authoritative zone returns normally",
			zones: []nbdns.CustomZone{{
				Domain:  "custom.zone.",
				Records: []nbdns.SimpleRecord{record},
			}},
			queryName:         "existing.custom.zone.",
			expectFallthrough: false,
			expectRecord:      true,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			resolver.Update(tc.zones)

			var responseMSG *dns.Msg
			responseWriter := &test.MockResponseWriter{
				WriteMsgFunc: func(m *dns.Msg) error {
					responseMSG = m
					return nil
				},
			}

			msg := new(dns.Msg).SetQuestion(tc.queryName, dns.TypeA)
			resolver.ServeDNS(responseWriter, msg)

			require.NotNil(t, responseMSG, "Should have received a response")

			if tc.expectFallthrough {
				assert.True(t, responseMSG.MsgHdr.Zero, "Zero bit should be set for fallthrough")
				assert.Equal(t, dns.RcodeNameError, responseMSG.Rcode, "Should return NXDOMAIN")
			} else {
				assert.False(t, responseMSG.MsgHdr.Zero, "Zero bit should not be set")
			}

			if tc.expectRecord {
				assert.Greater(t, len(responseMSG.Answer), 0, "Should have answer records")
				assert.Equal(t, dns.RcodeSuccess, responseMSG.Rcode)
			}
		})
	}
}

// TestLocalResolver_AuthoritativeFlag tests the AA flag behavior
func TestLocalResolver_AuthoritativeFlag(t *testing.T) {
	t.Run("direct record lookup is authoritative", func(t *testing.T) {
		resolver := NewResolver()
		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "host.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("host.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.True(t, resp.Authoritative)
	})

	t.Run("external resolution is not authoritative", func(t *testing.T) {
		resolver := NewResolver()
		resolver.resolver = &mockResolver{
			lookupFunc: func(_ context.Context, network, host string) ([]netip.Addr, error) {
				if host == "external.example.com." && network == "ip4" {
					return []netip.Addr{netip.MustParseAddr("93.184.216.34")}, nil
				}
				return nil, nil
			},
		}

		resolver.Update([]nbdns.CustomZone{{
			Domain: "test.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "external.example.com."},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.test.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Len(t, resp.Answer, 2)
		assert.False(t, resp.Authoritative)
	})
}

// TestLocalResolver_Stop tests cleanup on GracefullyStop
func TestLocalResolver_Stop(t *testing.T) {
	t.Run("GracefullyStop clears all state", func(t *testing.T) {
		resolver := NewResolver()
		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "host.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		resolver.Stop()

		msg := new(dns.Msg).SetQuestion("host.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Len(t, resp.Answer, 0)
		assert.False(t, resolver.isInManagedZone("host.example.com."))
	})

	t.Run("GracefullyStop is safe to call multiple times", func(t *testing.T) {
		resolver := NewResolver()
		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "host.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		resolver.Stop()
		resolver.Stop()
		resolver.Stop()
	})

	t.Run("GracefullyStop cancels in-flight external resolution", func(t *testing.T) {
		resolver := NewResolver()

		lookupStarted := make(chan struct{})
		lookupCtxCanceled := make(chan struct{})

		resolver.resolver = &mockResolver{
			lookupFunc: func(ctx context.Context, network, host string) ([]netip.Addr, error) {
				close(lookupStarted)
				<-ctx.Done()
				close(lookupCtxCanceled)
				return nil, ctx.Err()
			},
		}

		resolver.Update([]nbdns.CustomZone{{
			Domain: "test.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.test.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "external.example.com."},
			},
		}})

		done := make(chan struct{})
		go func() {
			msg := new(dns.Msg).SetQuestion("alias.test.", dns.TypeA)
			resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { return nil }}, msg)
			close(done)
		}()

		<-lookupStarted
		resolver.Stop()

		select {
		case <-lookupCtxCanceled:
		case <-time.After(time.Second):
			t.Fatal("external lookup context was not canceled")
		}

		select {
		case <-done:
		case <-time.After(time.Second):
			t.Fatal("ServeDNS did not return after Stop")
		}
	})
}

// TestLocalResolver_FallthroughCaseInsensitive verifies case-insensitive domain matching for fallthrough
func TestLocalResolver_FallthroughCaseInsensitive(t *testing.T) {
	resolver := NewResolver()

	resolver.Update([]nbdns.CustomZone{{
		Domain:           "EXAMPLE.COM.",
		Records:          []nbdns.SimpleRecord{{Name: "host.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "1.2.3.4"}},
		NonAuthoritative: true,
	}})

	var responseMSG *dns.Msg
	responseWriter := &test.MockResponseWriter{
		WriteMsgFunc: func(m *dns.Msg) error {
			responseMSG = m
			return nil
		},
	}

	msg := new(dns.Msg).SetQuestion("nonexistent.example.com.", dns.TypeA)
	resolver.ServeDNS(responseWriter, msg)

	require.NotNil(t, responseMSG)
	assert.True(t, responseMSG.MsgHdr.Zero, "Should fallthrough for non-authoritative zone with case-insensitive match")
}

// TestLocalResolver_WildcardCNAME tests wildcard CNAME record handling for non-CNAME queries
func TestLocalResolver_WildcardCNAME(t *testing.T) {
	t.Run("wildcard CNAME resolves A query with internal target", func(t *testing.T) {
		resolver := NewResolver()

		// Configure wildcard CNAME pointing to internal A record
		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("foo.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should resolve via wildcard CNAME")
		require.Len(t, resp.Answer, 2, "Should have CNAME + A record")

		// Verify CNAME has the original query name, not the wildcard
		cname, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok, "First answer should be CNAME")
		assert.Equal(t, "foo.example.com.", cname.Hdr.Name, "CNAME owner should be rewritten to query name")
		assert.Equal(t, "target.example.com.", cname.Target)

		// Verify A record
		a, ok := resp.Answer[1].(*dns.A)
		require.True(t, ok, "Second answer should be A record")
		assert.Equal(t, "10.0.0.1", a.A.String())
	})

	t.Run("wildcard CNAME resolves AAAA query with internal target", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("bar.example.com.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should resolve via wildcard CNAME")
		require.Len(t, resp.Answer, 2, "Should have CNAME + AAAA record")

		cname, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "bar.example.com.", cname.Hdr.Name, "CNAME owner should be rewritten")

		aaaa, ok := resp.Answer[1].(*dns.AAAA)
		require.True(t, ok)
		assert.Equal(t, "2001:db8::1", aaaa.AAAA.String())
	})

	t.Run("specific record takes precedence over wildcard CNAME", func(t *testing.T) {
		resolver := NewResolver()

		// Both wildcard CNAME and specific A record exist
		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "specific.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "192.168.1.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("specific.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1, "Should return specific A record only")

		a, ok := resp.Answer[0].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "192.168.1.1", a.A.String())
	})

	t.Run("specific CNAME takes precedence over wildcard CNAME", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "wildcard-target.example.com."},
				{Name: "specific.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "specific-target.example.com."},
				{Name: "specific-target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.1.1.1"},
				{Name: "wildcard-target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.2.2.2"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("specific.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.GreaterOrEqual(t, len(resp.Answer), 1)

		cname, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "specific-target.example.com.", cname.Target, "Should use specific CNAME, not wildcard")
	})

	t.Run("wildcard CNAME to non-existent internal target returns NXDOMAIN with CNAME", func(t *testing.T) {
		resolver := NewResolver()

		// Wildcard CNAME pointing to non-existent internal target
		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "nonexistent.example.com."},
			},
		}})

		msg := new(dns.Msg).SetQuestion("foo.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		// Per RFC 6604, CNAME chains should return the rcode of the final target.
		// When the wildcard CNAME target doesn't exist in the managed zone, this
		// returns NXDOMAIN with the CNAME record included.
		// Note: Current implementation returns NODATA (success) because the wildcard
		// domain exists. This test documents the actual behavior.
		if resp.Rcode == dns.RcodeNameError {
			// RFC-compliant behavior: NXDOMAIN with CNAME
			require.Len(t, resp.Answer, 1, "Should include the CNAME pointing to non-existent target")
			cname, ok := resp.Answer[0].(*dns.CNAME)
			require.True(t, ok)
			assert.Equal(t, "foo.example.com.", cname.Hdr.Name, "CNAME owner should be rewritten")
			assert.Equal(t, "nonexistent.example.com.", cname.Target)
		} else {
			// Current behavior: NODATA (success with CNAME but target not found)
			assert.Equal(t, dns.RcodeSuccess, resp.Rcode, "Returns NODATA when wildcard exists but target doesn't")
		}
	})

	t.Run("wildcard CNAME with multi-level subdomain", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		// Query with multi-level subdomain - wildcard should only match first label
		// Standard DNS wildcards only match a single label, so sub.domain.example.com
		// should NOT match *.example.com - this tests current implementation behavior
		msg := new(dns.Msg).SetQuestion("sub.domain.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
	})

	t.Run("wildcard CNAME NODATA when target has no matching type", func(t *testing.T) {
		resolver := NewResolver()

		// Wildcard CNAME to target that only has A record, query for AAAA
		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("foo.example.com.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should return NODATA (success with no answer for AAAA)")
		require.Len(t, resp.Answer, 1, "Should have only CNAME")

		cname, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "foo.example.com.", cname.Hdr.Name)
	})

	t.Run("direct CNAME query for wildcard record", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
			},
		}})

		// Direct CNAME query should also work via wildcard
		msg := new(dns.Msg).SetQuestion("foo.example.com.", dns.TypeCNAME)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1)

		cname, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "foo.example.com.", cname.Hdr.Name, "CNAME owner should be rewritten")
		assert.Equal(t, "target.example.com.", cname.Target)
	})

	t.Run("wildcard CNAME case insensitive query", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("FOO.EXAMPLE.COM.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode, "Wildcard CNAME should match case-insensitively")
		require.Len(t, resp.Answer, 2)
	})

	t.Run("wildcard A and wildcard CNAME coexist - A takes precedence", func(t *testing.T) {
		resolver := NewResolver()

		// Both wildcard A and wildcard CNAME exist
		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
			},
		}})

		msg := new(dns.Msg).SetQuestion("foo.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1)

		// A record should be returned, not CNAME
		a, ok := resp.Answer[0].(*dns.A)
		require.True(t, ok, "Wildcard A should take precedence over wildcard CNAME for A query")
		assert.Equal(t, "10.0.0.1", a.A.String())
	})

	t.Run("wildcard CNAME with chained CNAMEs", func(t *testing.T) {
		resolver := NewResolver()

		// Wildcard CNAME -> another CNAME -> A record
		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "hop1.example.com."},
				{Name: "hop1.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "final.example.com."},
				{Name: "final.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("anyhost.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 3, "Should have wildcard CNAME + hop1 CNAME + A record")

		// First should be the wildcard CNAME with rewritten name
		cname1, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "anyhost.example.com.", cname1.Hdr.Name)
		assert.Equal(t, "hop1.example.com.", cname1.Target)
	})
}

// TestLocalResolver_WildcardAandAAAA tests wildcard A and AAAA record handling
func TestLocalResolver_WildcardAandAAAA(t *testing.T) {
	t.Run("wildcard A record resolves with owner name rewriting", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("anyhost.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1)

		a, ok := resp.Answer[0].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "anyhost.example.com.", a.Hdr.Name, "Owner name should be rewritten to query name")
		assert.Equal(t, "10.0.0.1", a.A.String())
	})

	t.Run("wildcard AAAA record resolves with owner name rewriting", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("anyhost.example.com.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1)

		aaaa, ok := resp.Answer[0].(*dns.AAAA)
		require.True(t, ok)
		assert.Equal(t, "anyhost.example.com.", aaaa.Hdr.Name, "Owner name should be rewritten to query name")
		assert.Equal(t, "2001:db8::1", aaaa.AAAA.String())
	})

	t.Run("NODATA when querying AAAA but only wildcard A exists", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("anyhost.example.com.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should return NODATA (success with no answer)")
		assert.Len(t, resp.Answer, 0, "Should have no AAAA answer")
	})

	t.Run("NODATA when querying A but only wildcard AAAA exists", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("anyhost.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should return NODATA (success with no answer)")
		assert.Len(t, resp.Answer, 0, "Should have no A answer")
	})

	t.Run("dual-stack wildcard returns both A and AAAA separately", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "*.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		// Query A
		msgA := new(dns.Msg).SetQuestion("anyhost.example.com.", dns.TypeA)
		var respA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respA = m; return nil }}, msgA)

		require.NotNil(t, respA)
		require.Equal(t, dns.RcodeSuccess, respA.Rcode)
		require.Len(t, respA.Answer, 1)
		a, ok := respA.Answer[0].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "10.0.0.1", a.A.String())

		// Query AAAA
		msgAAAA := new(dns.Msg).SetQuestion("anyhost.example.com.", dns.TypeAAAA)
		var respAAAA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respAAAA = m; return nil }}, msgAAAA)

		require.NotNil(t, respAAAA)
		require.Equal(t, dns.RcodeSuccess, respAAAA.Rcode)
		require.Len(t, respAAAA.Answer, 1)
		aaaa, ok := respAAAA.Answer[0].(*dns.AAAA)
		require.True(t, ok)
		assert.Equal(t, "2001:db8::1", aaaa.AAAA.String())
	})

	t.Run("specific A takes precedence over wildcard A", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "specific.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "192.168.1.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("specific.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1)

		a, ok := resp.Answer[0].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "192.168.1.1", a.A.String(), "Specific record should take precedence")
	})

	t.Run("specific AAAA takes precedence over wildcard AAAA", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
				{Name: "specific.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::2"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("specific.example.com.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1)

		aaaa, ok := resp.Answer[0].(*dns.AAAA)
		require.True(t, ok)
		assert.Equal(t, "2001:db8::2", aaaa.AAAA.String(), "Specific record should take precedence")
	})

	t.Run("multiple wildcard A records round-robin", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.2"},
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.3"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("anyhost.example.com.", dns.TypeA)

		var firstIPs []string
		for i := 0; i < 3; i++ {
			var resp *dns.Msg
			resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

			require.NotNil(t, resp)
			require.Len(t, resp.Answer, 3, "Should return all 3 A records")

			a, ok := resp.Answer[0].(*dns.A)
			require.True(t, ok)
			firstIPs = append(firstIPs, a.A.String())

			// Verify owner name is rewritten for all records
			for _, ans := range resp.Answer {
				assert.Equal(t, "anyhost.example.com.", ans.Header().Name)
			}
		}

		// Verify rotation happened
		assert.NotEqual(t, firstIPs[0], firstIPs[1], "First record should rotate")
		assert.NotEqual(t, firstIPs[1], firstIPs[2], "Second rotation should differ")
	})

	t.Run("wildcard A case insensitive", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("ANYHOST.EXAMPLE.COM.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1)
	})

	t.Run("wildcard does not match multi-level subdomain", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		// *.example.com should NOT match sub.domain.example.com (standard DNS behavior)
		msg := new(dns.Msg).SetQuestion("sub.domain.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		// This depends on implementation - standard DNS wildcards only match single label
		// Current implementation replaces first label with *, so it WOULD match
		// This test documents the current behavior
	})

	t.Run("wildcard with existing domain but different type returns NODATA", func(t *testing.T) {
		resolver := NewResolver()

		// Specific A record exists, but query for TXT on wildcard domain
		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("test.example.com.", dns.TypeTXT)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should return NODATA for existing wildcard domain with different type")
		assert.Len(t, resp.Answer, 0)
	})

	t.Run("mixed specific and wildcard returns correct records", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "specific.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		// Query A for specific - should use wildcard
		msgA := new(dns.Msg).SetQuestion("specific.example.com.", dns.TypeA)
		var respA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respA = m; return nil }}, msgA)

		require.NotNil(t, respA)
		// This could be NODATA since specific.example.com exists but has no A
		// or could return wildcard A - depends on implementation
		// The current behavior returns NODATA because specific domain exists

		// Query AAAA for specific - should use specific record
		msgAAAA := new(dns.Msg).SetQuestion("specific.example.com.", dns.TypeAAAA)
		var respAAAA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respAAAA = m; return nil }}, msgAAAA)

		require.NotNil(t, respAAAA)
		require.Equal(t, dns.RcodeSuccess, respAAAA.Rcode)
		require.Len(t, respAAAA.Answer, 1)
		aaaa, ok := respAAAA.Answer[0].(*dns.AAAA)
		require.True(t, ok)
		assert.Equal(t, "2001:db8::1", aaaa.AAAA.String())
	})
}

// TestLocalResolver_WildcardEdgeCases tests edge cases for wildcard record handling
func TestLocalResolver_WildcardEdgeCases(t *testing.T) {
	t.Run("wildcard does not match NS queries", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("foo.example.com.", dns.TypeNS)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeNameError, resp.Rcode, "NS queries should not match wildcards")
		assert.Len(t, resp.Answer, 0)
	})

	t.Run("wildcard does not match SOA queries", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("foo.example.com.", dns.TypeSOA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeNameError, resp.Rcode, "SOA queries should not match wildcards")
		assert.Len(t, resp.Answer, 0)
	})

	t.Run("apex wildcard query", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		// Query for *.example.com directly (the wildcard itself)
		msg := new(dns.Msg).SetQuestion("*.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1)

		a, ok := resp.Answer[0].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "10.0.0.1", a.A.String())
	})

	t.Run("wildcard TXT record", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeTXT), Class: nbdns.DefaultClass, TTL: 300, RData: "v=spf1 -all"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("mail.example.com.", dns.TypeTXT)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1)

		txt, ok := resp.Answer[0].(*dns.TXT)
		require.True(t, ok)
		assert.Equal(t, "mail.example.com.", txt.Hdr.Name, "TXT owner should be rewritten")
	})

	t.Run("wildcard MX record", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeMX), Class: nbdns.DefaultClass, TTL: 300, RData: "10 mail.example.com."},
			},
		}})

		msg := new(dns.Msg).SetQuestion("sub.example.com.", dns.TypeMX)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1)

		mx, ok := resp.Answer[0].(*dns.MX)
		require.True(t, ok)
		assert.Equal(t, "sub.example.com.", mx.Hdr.Name, "MX owner should be rewritten")
	})

	t.Run("non-authoritative zone with wildcard CNAME triggers fallthrough for unmatched names", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain:           "example.com.",
			NonAuthoritative: true,
			Records: []nbdns.SimpleRecord{
				{Name: "*.sub.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
			},
		}})

		// Query for name not matching the wildcard pattern
		msg := new(dns.Msg).SetQuestion("other.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.True(t, resp.MsgHdr.Zero, "Should trigger fallthrough for non-authoritative zone")
	})
}

// TestLocalResolver_MixedRecordTypes tests scenarios with A, AAAA, and CNAME records combined
func TestLocalResolver_MixedRecordTypes(t *testing.T) {
	t.Run("specific A with wildcard CNAME - A query uses specific A", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "specific.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.2"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("specific.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1, "Should return only the specific A record")

		a, ok := resp.Answer[0].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "10.0.0.1", a.A.String(), "Should use specific A, not follow wildcard CNAME")
	})

	t.Run("specific AAAA with wildcard CNAME - AAAA query uses specific AAAA", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "specific.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
				{Name: "target.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::2"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("specific.example.com.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 1, "Should return only the specific AAAA record")

		aaaa, ok := resp.Answer[0].(*dns.AAAA)
		require.True(t, ok)
		assert.Equal(t, "2001:db8::1", aaaa.AAAA.String(), "Should use specific AAAA, not follow wildcard CNAME")
	})

	t.Run("specific A only - AAAA query returns NODATA", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "host.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("host.example.com.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should return NODATA (success with no AAAA)")
		assert.Len(t, resp.Answer, 0)
	})

	t.Run("specific AAAA only - A query returns NODATA", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "host.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("host.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should return NODATA (success with no A)")
		assert.Len(t, resp.Answer, 0)
	})

	t.Run("CNAME with both A and AAAA target - A query returns CNAME + A", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "target.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 2, "Should have CNAME + A")

		cname, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "target.example.com.", cname.Target)

		a, ok := resp.Answer[1].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "10.0.0.1", a.A.String())
	})

	t.Run("CNAME with both A and AAAA target - AAAA query returns CNAME + AAAA", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "target.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.example.com.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		require.Equal(t, dns.RcodeSuccess, resp.Rcode)
		require.Len(t, resp.Answer, 2, "Should have CNAME + AAAA")

		cname, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "target.example.com.", cname.Target)

		aaaa, ok := resp.Answer[1].(*dns.AAAA)
		require.True(t, ok)
		assert.Equal(t, "2001:db8::1", aaaa.AAAA.String())
	})

	t.Run("CNAME to target with only A - AAAA query returns CNAME only (NODATA)", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.example.com.", dns.TypeAAAA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should return NODATA with CNAME")
		require.Len(t, resp.Answer, 1, "Should have only CNAME")

		_, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
	})

	t.Run("CNAME to target with only AAAA - A query returns CNAME only (NODATA)", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		msg := new(dns.Msg).SetQuestion("alias.example.com.", dns.TypeA)
		var resp *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp = m; return nil }}, msg)

		require.NotNil(t, resp)
		assert.Equal(t, dns.RcodeSuccess, resp.Rcode, "Should return NODATA with CNAME")
		require.Len(t, resp.Answer, 1, "Should have only CNAME")

		_, ok := resp.Answer[0].(*dns.CNAME)
		require.True(t, ok)
	})

	t.Run("wildcard A + wildcard AAAA + wildcard CNAME - each query type returns correct record", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "*.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
			},
		}})

		// A query should return wildcard A (not CNAME)
		msgA := new(dns.Msg).SetQuestion("any.example.com.", dns.TypeA)
		var respA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respA = m; return nil }}, msgA)

		require.NotNil(t, respA)
		require.Equal(t, dns.RcodeSuccess, respA.Rcode)
		require.Len(t, respA.Answer, 1)
		a, ok := respA.Answer[0].(*dns.A)
		require.True(t, ok, "A query should return A record, not CNAME")
		assert.Equal(t, "10.0.0.1", a.A.String())

		// AAAA query should return wildcard AAAA (not CNAME)
		msgAAAA := new(dns.Msg).SetQuestion("any.example.com.", dns.TypeAAAA)
		var respAAAA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respAAAA = m; return nil }}, msgAAAA)

		require.NotNil(t, respAAAA)
		require.Equal(t, dns.RcodeSuccess, respAAAA.Rcode)
		require.Len(t, respAAAA.Answer, 1)
		aaaa, ok := respAAAA.Answer[0].(*dns.AAAA)
		require.True(t, ok, "AAAA query should return AAAA record, not CNAME")
		assert.Equal(t, "2001:db8::1", aaaa.AAAA.String())

		// CNAME query should return wildcard CNAME
		msgCNAME := new(dns.Msg).SetQuestion("any.example.com.", dns.TypeCNAME)
		var respCNAME *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respCNAME = m; return nil }}, msgCNAME)

		require.NotNil(t, respCNAME)
		require.Equal(t, dns.RcodeSuccess, respCNAME.Rcode)
		require.Len(t, respCNAME.Answer, 1)
		cname, ok := respCNAME.Answer[0].(*dns.CNAME)
		require.True(t, ok, "CNAME query should return CNAME record")
		assert.Equal(t, "target.example.com.", cname.Target)
	})

	t.Run("dual-stack host with both A and AAAA", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "host.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "host.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.2"},
				{Name: "host.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
				{Name: "host.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::2"},
			},
		}})

		// A query
		msgA := new(dns.Msg).SetQuestion("host.example.com.", dns.TypeA)
		var respA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respA = m; return nil }}, msgA)

		require.NotNil(t, respA)
		require.Equal(t, dns.RcodeSuccess, respA.Rcode)
		require.Len(t, respA.Answer, 2, "Should return both A records")
		for _, ans := range respA.Answer {
			_, ok := ans.(*dns.A)
			require.True(t, ok)
		}

		// AAAA query
		msgAAAA := new(dns.Msg).SetQuestion("host.example.com.", dns.TypeAAAA)
		var respAAAA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respAAAA = m; return nil }}, msgAAAA)

		require.NotNil(t, respAAAA)
		require.Equal(t, dns.RcodeSuccess, respAAAA.Rcode)
		require.Len(t, respAAAA.Answer, 2, "Should return both AAAA records")
		for _, ans := range respAAAA.Answer {
			_, ok := ans.(*dns.AAAA)
			require.True(t, ok)
		}
	})

	t.Run("CNAME chain with mixed record types at target", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "alias1.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "alias2.example.com."},
				{Name: "alias2.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "target.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		// A query through chain
		msgA := new(dns.Msg).SetQuestion("alias1.example.com.", dns.TypeA)
		var respA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respA = m; return nil }}, msgA)

		require.NotNil(t, respA)
		require.Equal(t, dns.RcodeSuccess, respA.Rcode)
		require.Len(t, respA.Answer, 3, "Should have 2 CNAMEs + 1 A")

		// Verify chain order
		cname1, ok := respA.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "alias2.example.com.", cname1.Target)

		cname2, ok := respA.Answer[1].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "target.example.com.", cname2.Target)

		a, ok := respA.Answer[2].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "10.0.0.1", a.A.String())

		// AAAA query through chain
		msgAAAA := new(dns.Msg).SetQuestion("alias1.example.com.", dns.TypeAAAA)
		var respAAAA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respAAAA = m; return nil }}, msgAAAA)

		require.NotNil(t, respAAAA)
		require.Equal(t, dns.RcodeSuccess, respAAAA.Rcode)
		require.Len(t, respAAAA.Answer, 3, "Should have 2 CNAMEs + 1 AAAA")

		aaaa, ok := respAAAA.Answer[2].(*dns.AAAA)
		require.True(t, ok)
		assert.Equal(t, "2001:db8::1", aaaa.AAAA.String())
	})

	t.Run("wildcard CNAME with dual-stack target", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "*.example.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.example.com."},
				{Name: "target.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "target.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		// A query via wildcard CNAME
		msgA := new(dns.Msg).SetQuestion("any.example.com.", dns.TypeA)
		var respA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respA = m; return nil }}, msgA)

		require.NotNil(t, respA)
		require.Equal(t, dns.RcodeSuccess, respA.Rcode)
		require.Len(t, respA.Answer, 2, "Should have CNAME + A")

		cname, ok := respA.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "any.example.com.", cname.Hdr.Name, "CNAME owner should be rewritten")

		a, ok := respA.Answer[1].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "10.0.0.1", a.A.String())

		// AAAA query via wildcard CNAME
		msgAAAA := new(dns.Msg).SetQuestion("other.example.com.", dns.TypeAAAA)
		var respAAAA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respAAAA = m; return nil }}, msgAAAA)

		require.NotNil(t, respAAAA)
		require.Equal(t, dns.RcodeSuccess, respAAAA.Rcode)
		require.Len(t, respAAAA.Answer, 2, "Should have CNAME + AAAA")

		cname2, ok := respAAAA.Answer[0].(*dns.CNAME)
		require.True(t, ok)
		assert.Equal(t, "other.example.com.", cname2.Hdr.Name, "CNAME owner should be rewritten")

		aaaa, ok := respAAAA.Answer[1].(*dns.AAAA)
		require.True(t, ok)
		assert.Equal(t, "2001:db8::1", aaaa.AAAA.String())
	})

	t.Run("specific A + wildcard AAAA - each query type returns correct record", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{{
			Domain: "example.com.",
			Records: []nbdns.SimpleRecord{
				{Name: "host.example.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.0.0.1"},
				{Name: "*.example.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8::1"},
			},
		}})

		// A query for host should return specific A
		msgA := new(dns.Msg).SetQuestion("host.example.com.", dns.TypeA)
		var respA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respA = m; return nil }}, msgA)

		require.NotNil(t, respA)
		require.Equal(t, dns.RcodeSuccess, respA.Rcode)
		require.Len(t, respA.Answer, 1)
		a, ok := respA.Answer[0].(*dns.A)
		require.True(t, ok)
		assert.Equal(t, "10.0.0.1", a.A.String())

		// AAAA query for host should return NODATA (specific A exists, no AAAA for host.example.com)
		msgAAAA := new(dns.Msg).SetQuestion("host.example.com.", dns.TypeAAAA)
		var respAAAA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respAAAA = m; return nil }}, msgAAAA)

		require.NotNil(t, respAAAA)
		// RFC 4592 section 2.2.1: wildcard should NOT match when the name EXISTS in zone.
		// host.example.com exists (has A record), so AAAA query returns NODATA, not wildcard.
		assert.Equal(t, dns.RcodeSuccess, respAAAA.Rcode, "Should return NODATA for existing host without AAAA")
		assert.Len(t, respAAAA.Answer, 0, "RFC 4592: wildcard should not match when name exists")

		// AAAA query for other host should return wildcard AAAA
		msgAAAAOther := new(dns.Msg).SetQuestion("other.example.com.", dns.TypeAAAA)
		var respAAAAOther *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { respAAAAOther = m; return nil }}, msgAAAAOther)

		require.NotNil(t, respAAAAOther)
		require.Equal(t, dns.RcodeSuccess, respAAAAOther.Rcode)
		require.Len(t, respAAAAOther.Answer, 1)
		aaaa, ok := respAAAAOther.Answer[0].(*dns.AAAA)
		require.True(t, ok)
		assert.Equal(t, "2001:db8::1", aaaa.AAAA.String())
		assert.Equal(t, "other.example.com.", aaaa.Hdr.Name, "Owner should be rewritten")
	})

	t.Run("multiple zones with mixed records", func(t *testing.T) {
		resolver := NewResolver()

		resolver.Update([]nbdns.CustomZone{
			{
				Domain: "zone1.com.",
				Records: []nbdns.SimpleRecord{
					{Name: "host.zone1.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.1.0.1"},
					{Name: "host.zone1.com.", Type: int(dns.TypeAAAA), Class: nbdns.DefaultClass, TTL: 300, RData: "2001:db8:1::1"},
				},
			},
			{
				Domain: "zone2.com.",
				Records: []nbdns.SimpleRecord{
					{Name: "alias.zone2.com.", Type: int(dns.TypeCNAME), Class: nbdns.DefaultClass, TTL: 300, RData: "target.zone2.com."},
					{Name: "target.zone2.com.", Type: int(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.2.0.1"},
				},
			},
		})

		// Query zone1 A
		msg1A := new(dns.Msg).SetQuestion("host.zone1.com.", dns.TypeA)
		var resp1A *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp1A = m; return nil }}, msg1A)

		require.NotNil(t, resp1A)
		require.Equal(t, dns.RcodeSuccess, resp1A.Rcode)
		require.Len(t, resp1A.Answer, 1)

		// Query zone1 AAAA
		msg1AAAA := new(dns.Msg).SetQuestion("host.zone1.com.", dns.TypeAAAA)
		var resp1AAAA *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp1AAAA = m; return nil }}, msg1AAAA)

		require.NotNil(t, resp1AAAA)
		require.Equal(t, dns.RcodeSuccess, resp1AAAA.Rcode)
		require.Len(t, resp1AAAA.Answer, 1)

		// Query zone2 via CNAME
		msg2A := new(dns.Msg).SetQuestion("alias.zone2.com.", dns.TypeA)
		var resp2A *dns.Msg
		resolver.ServeDNS(&test.MockResponseWriter{WriteMsgFunc: func(m *dns.Msg) error { resp2A = m; return nil }}, msg2A)

		require.NotNil(t, resp2A)
		require.Equal(t, dns.RcodeSuccess, resp2A.Rcode)
		require.Len(t, resp2A.Answer, 2, "Should have CNAME + A")
	})
}

// BenchmarkFindZone_BestCase benchmarks zone lookup with immediate match (first label)
func BenchmarkFindZone_BestCase(b *testing.B) {
	resolver := NewResolver()

	// Single zone that matches immediately
	resolver.Update([]nbdns.CustomZone{{
		Domain:           "example.com.",
		NonAuthoritative: true,
	}})

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		resolver.shouldFallthrough("example.com.")
	}
}

// BenchmarkFindZone_WorstCase benchmarks zone lookup with many zones, no match, many labels
func BenchmarkFindZone_WorstCase(b *testing.B) {
	resolver := NewResolver()

	// 100 zones that won't match
	var zones []nbdns.CustomZone
	for i := 0; i < 100; i++ {
		zones = append(zones, nbdns.CustomZone{
			Domain:           fmt.Sprintf("zone%d.internal.", i),
			NonAuthoritative: true,
		})
	}
	resolver.Update(zones)

	// Query with many labels that won't match any zone
	qname := "a.b.c.d.e.f.g.h.external.com."

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		resolver.shouldFallthrough(qname)
	}
}

// BenchmarkFindZone_TypicalCase benchmarks typical usage: few zones, subdomain match
func BenchmarkFindZone_TypicalCase(b *testing.B) {
	resolver := NewResolver()

	// Typical setup: peer zone (authoritative) + one user zone (non-authoritative)
	resolver.Update([]nbdns.CustomZone{
		{Domain: "netbird.cloud.", NonAuthoritative: false},
		{Domain: "custom.local.", NonAuthoritative: true},
	})

	// Query for subdomain of user zone
	qname := "myhost.custom.local."

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		resolver.shouldFallthrough(qname)
	}
}

// BenchmarkIsInManagedZone_ManyZones benchmarks isInManagedZone with 100 zones
func BenchmarkIsInManagedZone_ManyZones(b *testing.B) {
	resolver := NewResolver()

	var zones []nbdns.CustomZone
	for i := 0; i < 100; i++ {
		zones = append(zones, nbdns.CustomZone{
			Domain: fmt.Sprintf("zone%d.internal.", i),
		})
	}
	resolver.Update(zones)

	// Query that matches zone50
	qname := "host.zone50.internal."

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		resolver.isInManagedZone(qname)
	}
}

// TestLocalResolver_FilterDisconnectedPeerAnswers verifies the
// connectivity-aware filtering layered on top of lookupRecords:
// when an A record's IP belongs to a known peer that's disconnected,
// the record is dropped from the answer. Records for unknown IPs pass
// through. If filtering would empty the answer entirely and at least
// one record was dropped, the original list is restored (escape hatch
// for the "all proxies offline" case).
func TestLocalResolver_FilterDisconnectedPeerAnswers(t *testing.T) {
	zone := "svc.cluster.netbird."
	connectedRec := nbdns.SimpleRecord{
		Name:  zone,
		Type:  int(dns.TypeA),
		Class: nbdns.DefaultClass,
		TTL:   5,
		RData: "100.64.0.10",
	}
	disconnectedRec := nbdns.SimpleRecord{
		Name:  zone,
		Type:  int(dns.TypeA),
		Class: nbdns.DefaultClass,
		TTL:   5,
		RData: "100.64.0.11",
	}
	unknownRec := nbdns.SimpleRecord{
		Name:  zone,
		Type:  int(dns.TypeA),
		Class: nbdns.DefaultClass,
		TTL:   5,
		RData: "203.0.113.5",
	}

	type ipState struct{ known, connected bool }
	tests := []struct {
		name        string
		records     []nbdns.SimpleRecord
		connByIP    map[string]ipState
		wantInOrder []string
	}{
		{
			name:    "drops disconnected peer, keeps connected",
			records: []nbdns.SimpleRecord{connectedRec, disconnectedRec},
			connByIP: map[string]ipState{
				"100.64.0.10": {known: true, connected: true},
				"100.64.0.11": {known: true, connected: false},
			},
			wantInOrder: []string{"100.64.0.10"},
		},
		{
			name:    "unknown IPs pass through untouched",
			records: []nbdns.SimpleRecord{unknownRec, disconnectedRec},
			connByIP: map[string]ipState{
				"100.64.0.11": {known: true, connected: false},
			},
			wantInOrder: []string{"203.0.113.5"},
		},
		{
			name:    "all disconnected falls back to original list",
			records: []nbdns.SimpleRecord{disconnectedRec, connectedRec},
			connByIP: map[string]ipState{
				"100.64.0.10": {known: true, connected: false},
				"100.64.0.11": {known: true, connected: false},
			},
			wantInOrder: []string{"100.64.0.11", "100.64.0.10"},
		},
		{
			name:        "no checker wired returns all records",
			records:     []nbdns.SimpleRecord{connectedRec, disconnectedRec},
			connByIP:    nil,
			wantInOrder: []string{"100.64.0.10", "100.64.0.11"},
		},
		{
			// A single answer is never filtered: dropping it would only
			// trigger the empty-answer escape hatch, so the fast path
			// returns it untouched.
			name:    "single disconnected answer passes through",
			records: []nbdns.SimpleRecord{disconnectedRec},
			connByIP: map[string]ipState{
				"100.64.0.11": {known: true, connected: false},
			},
			wantInOrder: []string{"100.64.0.11"},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			resolver := NewResolver()
			if tc.connByIP != nil {
				cm := mockPeerConnectivity{byIP: make(map[string]struct{ known, connected bool }, len(tc.connByIP))}
				for ip, st := range tc.connByIP {
					cm.byIP[ip] = struct{ known, connected bool }{st.known, st.connected}
				}
				resolver.SetPeerConnectivity(cm)
			}
			resolver.Update([]nbdns.CustomZone{{
				Domain:           strings.TrimSuffix(zone, "."),
				Records:          tc.records,
				NonAuthoritative: true,
			}})

			var got *dns.Msg
			writer := &test.MockResponseWriter{
				WriteMsgFunc: func(m *dns.Msg) error {
					got = m
					return nil
				},
			}
			req := new(dns.Msg).SetQuestion(zone, dns.TypeA)
			resolver.ServeDNS(writer, req)

			require.NotNil(t, got, "resolver must produce a response")
			require.Len(t, got.Answer, len(tc.wantInOrder),
				"answer count must match expected: %v", tc.wantInOrder)
			for i, want := range tc.wantInOrder {
				a, ok := got.Answer[i].(*dns.A)
				require.True(t, ok, "answer[%d] must be an A record", i)
				assert.Equal(t, want, a.A.String(),
					"answer[%d] expected %s got %s", i, want, a.A.String())
			}
		})
	}
}
