package file

import (
	"strconv"
	"strings"
	"testing"

	"github.com/coredns/coredns/plugin/file/tree"

	"github.com/miekg/dns"
)

func TestNameFromRight(t *testing.T) {
	z := NewZone("example.org.", "stdin")

	tests := []struct {
		in       string
		labels   int
		shot     bool
		expected string
	}{
		{"example.org.", 0, false, "example.org."},
		{"a.example.org.", 0, false, "example.org."},
		{"a.example.org.", 1, false, "a.example.org."},
		{"a.example.org.", 2, true, "a.example.org."},
		{"a.b.example.org.", 2, false, "a.b.example.org."},
	}

	for i, tc := range tests {
		got, shot := z.nameFromRight(tc.in, tc.labels)
		if got != tc.expected {
			t.Errorf("Test %d: expected %s, got %s", i, tc.expected, got)
		}
		if shot != tc.shot {
			t.Errorf("Test %d: expected shot to be %t, got %t", i, tc.shot, shot)
		}
	}
}

// Benchmark: measure performance across representative inputs and overshoot cases.
func BenchmarkNameFromRight(b *testing.B) {
	origin := "example.org."
	a := &Zone{origin: origin, origLen: dns.CountLabel(origin)}

	cases := []struct {
		name  string
		qname string
		i     int
	}{
		{"i0_origin", origin, 0},
		{"eq_origin_i1_shot", origin, 1},
		{"two_labels_i1", "a.b." + origin, 1},
		{"two_labels_i2", "a.b." + origin, 2},
		{"two_labels_i3_shot", "a.b." + origin, 3},
		{"ten_labels_i5", strings.Repeat("a.", 10) + origin, 5},
		{"ten_labels_i11_shot", strings.Repeat("a.", 10) + origin, 11},
		{"not_subdomain_shot", "other.tld.", 1},
	}

	var sink int
	for _, tc := range cases {
		b.Run(tc.name, func(b *testing.B) {
			qn, i := tc.qname, tc.i
			for b.Loop() {
				s, shot := a.nameFromRight(qn, i)
				sink += len(s)
				if shot {
					sink++
				}
			}
		})
	}
	if sink == 42 { // prevent elimination
		b.Log(sink)
	}
}

// BenchmarkNameFromRightRandomized iterates over a prebuilt pool
// of qnames and i values to emulate mixed workloads.
func BenchmarkNameFromRightRandomized(b *testing.B) {
	origin := "example.org."
	a := &Zone{origin: origin, origLen: dns.CountLabel(origin)}

	const poolSize = 1024
	type pair struct {
		q string
		i int
	}
	pool := make([]pair, 0, poolSize)

	// Build a variety of qnames with 1..8 labels before origin and various i, including overshoot.
	for n := 1; n <= 8; n++ {
		var sb strings.Builder
		for k := range n {
			sb.WriteString("l")
			sb.WriteString(strconv.Itoa(k))
			sb.WriteByte('.')
		}
		sb.WriteString(origin)
		q := sb.String()
		for i := 0; i <= n+2; i++ {
			pool = append(pool, pair{q: q, i: i})
		}
	}
	// Add some non-subdomain and shorter-than-origin cases.
	pool = append(pool, pair{"org.", 1}, pair{"other.tld.", 1})

	// Ensure pool length is power-of-two for fast masking; if not, trim.
	// Here we just rely on modulo since the pool isn't huge.

	b.ReportAllocs()
	var sink int
	for n := range b.N {
		p := pool[n%len(pool)]
		s, shot := a.nameFromRight(p.q, p.i)
		sink += len(s)
		if shot {
			sink++
		}
	}
	if sink == 43 {
		b.Log(sink)
	}
}

func TestInsertPreservesSRVCase(t *testing.T) {
	z := NewZone("home.arpa.", "stdin")

	// SRV with mixed case and space-escaped instance name
	srv, err := dns.NewRR(`Home\032Media._smb._tcp.home.arpa. 5 IN SRV 0 0 445 samba.home.arpa.`)
	if err != nil {
		t.Fatalf("Failed to parse SRV RR: %v", err)
	}

	if err := z.Insert(srv); err != nil {
		t.Fatalf("Insert failed: %v", err)
	}

	found := false
	err = z.Walk(func(_elem *tree.Elem, rrsets map[uint16][]dns.RR) error {
		for _, rrs := range rrsets {
			for _, rr := range rrs {
				if srvRR, ok := rr.(*dns.SRV); ok {
					if srvRR.Hdr.Name == "Home\\032Media._smb._tcp.home.arpa." {
						found = true
						if srvRR.Target != "samba.home.arpa." {
							t.Errorf("Expected SRV target to be 'samba.home.arpa.', got %q", srvRR.Target)
						}
					}
				}
			}
		}
		return nil
	})
	if err != nil {
		t.Fatalf("Tree walk failed: %v", err)
	}

	if !found {
		t.Errorf("SRV record with original case not found in tree")
	}
}
