// This file is part of the happyDomain (R) project.
// Copyright (c) 2020-2025 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// This program is offered under a commercial and under the AGPL license.
// For commercial licensing, contact us at <contact@happydomain.org>.
//
// For AGPL licensing:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

package helpers

import (
	"reflect"
	"testing"

	"github.com/miekg/dns"
)

func TestDomainFQDN(t *testing.T) {
	tests := []struct {
		name      string
		subdomain string
		origin    string
		expected  string
	}{
		{
			name:      "already FQDN",
			subdomain: "www.example.com.",
			origin:    "example.com.",
			expected:  "www.example.com.",
		},
		{
			name:      "relative subdomain",
			subdomain: "www",
			origin:    "example.com.",
			expected:  "www.example.com.",
		},
		{
			name:      "empty subdomain",
			subdomain: "",
			origin:    "example.com.",
			expected:  "example.com.",
		},
		{
			name:      "@ subdomain",
			subdomain: "@",
			origin:    "example.com.",
			expected:  "example.com.",
		},
		{
			name:      "multi-level subdomain",
			subdomain: "api.v1",
			origin:    "example.com.",
			expected:  "api.v1.example.com.",
		},
		{
			name:      "origin without trailing dot",
			subdomain: "www",
			origin:    "example.com",
			expected:  "www.example.com",
		},
		{
			name:      "empty with origin without trailing dot",
			subdomain: "",
			origin:    "example.com",
			expected:  "example.com",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := DomainFQDN(tt.subdomain, tt.origin)
			if result != tt.expected {
				t.Errorf("DomainFQDN(%q, %q) = %q, want %q", tt.subdomain, tt.origin, result, tt.expected)
			}
		})
	}
}

func TestDomainJoin(t *testing.T) {
	tests := []struct {
		name     string
		domains  []string
		expected string
	}{
		{
			name:     "two domains",
			domains:  []string{"www", "example.com"},
			expected: "www.example.com",
		},
		{
			name:     "three domains",
			domains:  []string{"api", "v1", "example.com"},
			expected: "api.v1.example.com",
		},
		{
			name:     "single domain",
			domains:  []string{"example.com"},
			expected: "example.com",
		},
		{
			name:     "empty domain in middle",
			domains:  []string{"www", "", "example.com"},
			expected: "www.example.com",
		},
		{
			name:     "@ symbol stops joining",
			domains:  []string{"www", "@", "example.com"},
			expected: "www",
		},
		{
			name:     "FQDN stops joining",
			domains:  []string{"www", "example.com.", "ignored.com"},
			expected: "www.example.com.",
		},
		{
			name:     "all empty",
			domains:  []string{"", "", ""},
			expected: "",
		},
		{
			name:     "@ at start",
			domains:  []string{"@", "example.com"},
			expected: "",
		},
		{
			name:     "empty slice",
			domains:  []string{},
			expected: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := DomainJoin(tt.domains...)
			if result != tt.expected {
				t.Errorf("DomainJoin(%v) = %q, want %q", tt.domains, result, tt.expected)
			}
		})
	}
}

func TestDomainRelative(t *testing.T) {
	tests := []struct {
		name      string
		subdomain string
		origin    string
		expected  string
	}{
		{
			name:      "full FQDN relative to origin",
			subdomain: "www.example.com.",
			origin:    "example.com.",
			expected:  "www",
		},
		{
			name:      "full FQDN without trailing dot relative to origin",
			subdomain: "www.example.com",
			origin:    "example.com.",
			expected:  "www.example.com",
		},
		{
			name:      "origin without trailing dot",
			subdomain: "www.example.com.",
			origin:    "example.com",
			expected:  "www",
		},
		{
			name:      "subdomain equals origin",
			subdomain: "example.com.",
			origin:    "example.com.",
			expected:  "@",
		},
		{
			name:      "subdomain equals origin without dots",
			subdomain: "example.com",
			origin:    "example.com",
			expected:  "example.com",
		},
		{
			name:      "not relative to origin",
			subdomain: "www.other.com.",
			origin:    "example.com.",
			expected:  "www.other.com.",
		},
		{
			name:      "multi-level subdomain",
			subdomain: "api.v1.example.com.",
			origin:    "example.com.",
			expected:  "api.v1",
		},
		{
			name:      "already relative",
			subdomain: "www",
			origin:    "example.com.",
			expected:  "www",
		},
		{
			name:      "empty becomes @",
			subdomain: "",
			origin:    "example.com.",
			expected:  "@",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := DomainRelative(tt.subdomain, tt.origin)
			if result != tt.expected {
				t.Errorf("DomainRelative(%q, %q) = %q, want %q", tt.subdomain, tt.origin, result, tt.expected)
			}
		})
	}
}

func TestNewRecord(t *testing.T) {
	tests := []struct {
		name     string
		domain   string
		rrtype   string
		ttl      uint32
		origin   string
		validate func(t *testing.T, rr dns.RR)
	}{
		{
			name:   "A record with relative domain",
			domain: "www",
			rrtype: "A",
			ttl:    3600,
			origin: "example.com.",
			validate: func(t *testing.T, rr dns.RR) {
				if rr.Header().Name != "www.example.com." {
					t.Errorf("Expected Name to be 'www.example.com.', got %q", rr.Header().Name)
				}
				if rr.Header().Rrtype != dns.TypeA {
					t.Errorf("Expected Rrtype to be TypeA, got %d", rr.Header().Rrtype)
				}
				if rr.Header().Ttl != 3600 {
					t.Errorf("Expected TTL to be 3600, got %d", rr.Header().Ttl)
				}
				if rr.Header().Class != dns.ClassINET {
					t.Errorf("Expected Class to be ClassINET, got %d", rr.Header().Class)
				}
			},
		},
		{
			name:   "AAAA record with absolute domain",
			domain: "www.example.com.",
			rrtype: "AAAA",
			ttl:    7200,
			origin: "example.com.",
			validate: func(t *testing.T, rr dns.RR) {
				if rr.Header().Name != "www.example.com." {
					t.Errorf("Expected Name to be 'www.example.com.', got %q", rr.Header().Name)
				}
				if rr.Header().Rrtype != dns.TypeAAAA {
					t.Errorf("Expected Rrtype to be TypeAAAA, got %d", rr.Header().Rrtype)
				}
				if rr.Header().Ttl != 7200 {
					t.Errorf("Expected TTL to be 7200, got %d", rr.Header().Ttl)
				}
			},
		},
		{
			name:   "MX record",
			domain: "@",
			rrtype: "MX",
			ttl:    1800,
			origin: "example.com.",
			validate: func(t *testing.T, rr dns.RR) {
				if rr.Header().Name != "example.com." {
					t.Errorf("Expected Name to be 'example.com.', got %q", rr.Header().Name)
				}
				if rr.Header().Rrtype != dns.TypeMX {
					t.Errorf("Expected Rrtype to be TypeMX, got %d", rr.Header().Rrtype)
				}
			},
		},
		{
			name:   "TXT record",
			domain: "_dmarc",
			rrtype: "TXT",
			ttl:    300,
			origin: "example.com.",
			validate: func(t *testing.T, rr dns.RR) {
				if rr.Header().Name != "_dmarc.example.com." {
					t.Errorf("Expected Name to be '_dmarc.example.com.', got %q", rr.Header().Name)
				}
				if rr.Header().Rrtype != dns.TypeTXT {
					t.Errorf("Expected Rrtype to be TypeTXT, got %d", rr.Header().Rrtype)
				}
			},
		},
		{
			name:   "CNAME record",
			domain: "www",
			rrtype: "CNAME",
			ttl:    600,
			origin: "example.com.",
			validate: func(t *testing.T, rr dns.RR) {
				if rr.Header().Rrtype != dns.TypeCNAME {
					t.Errorf("Expected Rrtype to be TypeCNAME, got %d", rr.Header().Rrtype)
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := NewRecord(tt.domain, tt.rrtype, tt.ttl, tt.origin)
			if result == nil {
				t.Fatal("NewRecord returned nil")
			}
			if dnsrr, ok := result.(dns.RR); ok {
				tt.validate(t, dnsrr)
			} else {
				t.Error("Result is not a dns.RR")
			}
		})
	}
}

func TestParseRecord(t *testing.T) {
	tests := []struct {
		name      string
		input     string
		origin    string
		wantError bool
		validate  func(t *testing.T, rr dns.RR)
	}{
		{
			name:      "SOA record",
			input:     "@ 3600	IN	SOA a.misconfigured.dns.server.invalid. hostmaster 2025091001 10800 3600 604800 3600",
			origin:    "example.com.",
			wantError: false,
			validate: func(t *testing.T, rr dns.RR) {
				soa, ok := rr.(*dns.SOA)
				if !ok {
					t.Fatal("Expected *dns.SOA type")
				}
				if soa.Ns != "a.misconfigured.dns.server.invalid." {
					t.Errorf("Expected Ns a.misconfigured.dns.server.invalid., got %s", soa.Ns)
				}
				if soa.Mbox != "hostmaster.example.com." {
					t.Errorf("Expected Mbox hostmaster.example.com., got %s", soa.Mbox)
				}
				if soa.Header().Ttl != 3600 {
					t.Errorf("Expected TTL 3600, got %d", soa.Header().Ttl)
				}
			},
		},
		{
			name:      "A record",
			input:     "www 3600 IN A 192.0.2.1",
			origin:    "example.com.",
			wantError: false,
			validate: func(t *testing.T, rr dns.RR) {
				a, ok := rr.(*dns.A)
				if !ok {
					t.Fatal("Expected *dns.A type")
				}
				if a.A.String() != "192.0.2.1" {
					t.Errorf("Expected IP 192.0.2.1, got %s", a.A.String())
				}
				if a.Header().Ttl != 3600 {
					t.Errorf("Expected TTL 3600, got %d", a.Header().Ttl)
				}
			},
		},
		{
			name:      "AAAA record",
			input:     "www 7200 IN AAAA 2001:db8::1",
			origin:    "example.com.",
			wantError: false,
			validate: func(t *testing.T, rr dns.RR) {
				aaaa, ok := rr.(*dns.AAAA)
				if !ok {
					t.Fatal("Expected *dns.AAAA type")
				}
				if aaaa.AAAA.String() != "2001:db8::1" {
					t.Errorf("Expected IP 2001:db8::1, got %s", aaaa.AAAA.String())
				}
			},
		},
		{
			name:      "MX record",
			input:     "@ 1800 IN MX 10 mail.example.com.",
			origin:    "example.com.",
			wantError: false,
			validate: func(t *testing.T, rr dns.RR) {
				mx, ok := rr.(*dns.MX)
				if !ok {
					t.Fatal("Expected *dns.MX type")
				}
				if mx.Preference != 10 {
					t.Errorf("Expected preference 10, got %d", mx.Preference)
				}
				if mx.Mx != "mail.example.com." {
					t.Errorf("Expected Mx 'mail.example.com.', got %q", mx.Mx)
				}
			},
		},
		{
			name:      "CNAME record",
			input:     "www 600 IN CNAME target.example.com.",
			origin:    "example.com.",
			wantError: false,
			validate: func(t *testing.T, rr dns.RR) {
				cname, ok := rr.(*dns.CNAME)
				if !ok {
					t.Fatal("Expected *dns.CNAME type")
				}
				if cname.Target != "target.example.com." {
					t.Errorf("Expected Target 'target.example.com.', got %q", cname.Target)
				}
			},
		},
		{
			name:      "TXT record with single value",
			input:     "_dmarc 300 IN TXT \"v=DMARC1; p=none\"",
			origin:    "example.com.",
			wantError: false,
			validate: func(t *testing.T, rr dns.RR) {
				// ParseRecord returns happydns.TXT for TXT records
				if rr.Header().Rrtype != dns.TypeTXT {
					t.Errorf("Expected TypeTXT, got %d", rr.Header().Rrtype)
				}
			},
		},
		{
			name:      "NS record",
			input:     "@ 86400 IN NS ns1.example.com.",
			origin:    "example.com.",
			wantError: false,
			validate: func(t *testing.T, rr dns.RR) {
				ns, ok := rr.(*dns.NS)
				if !ok {
					t.Fatal("Expected *dns.NS type")
				}
				if ns.Ns != "ns1.example.com." {
					t.Errorf("Expected Ns 'ns1.example.com.', got %q", ns.Ns)
				}
			},
		},
		{
			name:      "SRV record",
			input:     "_http._tcp 3600 IN SRV 10 60 80 target.example.com.",
			origin:    "example.com.",
			wantError: false,
			validate: func(t *testing.T, rr dns.RR) {
				srv, ok := rr.(*dns.SRV)
				if !ok {
					t.Fatal("Expected *dns.SRV type")
				}
				if srv.Priority != 10 {
					t.Errorf("Expected Priority 10, got %d", srv.Priority)
				}
				if srv.Weight != 60 {
					t.Errorf("Expected Weight 60, got %d", srv.Weight)
				}
				if srv.Port != 80 {
					t.Errorf("Expected Port 80, got %d", srv.Port)
				}
				if srv.Target != "target.example.com." {
					t.Errorf("Expected Target 'target.example.com.', got %q", srv.Target)
				}
			},
		},
		{
			name:      "invalid record",
			input:     "invalid record format",
			origin:    "example.com.",
			wantError: true,
			validate:  nil,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := ParseRecord(tt.input, tt.origin)
			if tt.wantError {
				if err == nil {
					t.Error("Expected error, got nil")
				}
				return
			}
			if err != nil {
				t.Fatalf("Unexpected error: %v", err)
			}
			if result == nil {
				t.Fatal("ParseRecord returned nil")
			}
			if tt.validate != nil {
				if dnsrr, ok := result.(dns.RR); ok {
					tt.validate(t, dnsrr)
				}
			}
		})
	}
}

func TestRRRelative(t *testing.T) {
	origin := "example.com."

	tests := []struct {
		name     string
		input    dns.RR
		origin   string
		validate func(t *testing.T, rr dns.RR)
	}{
		{
			name: "A record",
			input: &dns.A{
				Hdr: dns.RR_Header{
					Name:   "www.example.com.",
					Rrtype: dns.TypeA,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				A: []byte{192, 0, 2, 1},
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				if rr.Header().Name != "www" {
					t.Errorf("Expected Name to be 'www', got %q", rr.Header().Name)
				}
			},
		},
		{
			name: "NS record",
			input: &dns.NS{
				Hdr: dns.RR_Header{
					Name:   "example.com.",
					Rrtype: dns.TypeNS,
					Class:  dns.ClassINET,
					Ttl:    86400,
				},
				Ns: "ns1.example.com.",
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				ns := rr.(*dns.NS)
				if ns.Header().Name != "" {
					t.Errorf("Expected Name to be '', got %q", ns.Header().Name)
				}
				if ns.Ns != "ns1" {
					t.Errorf("Expected Ns to be 'ns1', got %q", ns.Ns)
				}
			},
		},
		{
			name: "MX record",
			input: &dns.MX{
				Hdr: dns.RR_Header{
					Name:   "example.com.",
					Rrtype: dns.TypeMX,
					Class:  dns.ClassINET,
					Ttl:    1800,
				},
				Preference: 10,
				Mx:         "mail.example.com.",
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				mx := rr.(*dns.MX)
				if mx.Mx != "mail" {
					t.Errorf("Expected Mx to be 'mail', got %q", mx.Mx)
				}
			},
		},
		{
			name: "CNAME record",
			input: &dns.CNAME{
				Hdr: dns.RR_Header{
					Name:   "www.example.com.",
					Rrtype: dns.TypeCNAME,
					Class:  dns.ClassINET,
					Ttl:    600,
				},
				Target: "target.example.com.",
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				cname := rr.(*dns.CNAME)
				if cname.Target != "target" {
					t.Errorf("Expected Target to be 'target', got %q", cname.Target)
				}
			},
		},
		{
			name: "SRV record",
			input: &dns.SRV{
				Hdr: dns.RR_Header{
					Name:   "_http._tcp.example.com.",
					Rrtype: dns.TypeSRV,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				Priority: 10,
				Weight:   60,
				Port:     80,
				Target:   "server.example.com.",
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				srv := rr.(*dns.SRV)
				if srv.Target != "server" {
					t.Errorf("Expected Target to be 'server', got %q", srv.Target)
				}
				if srv.Header().Name != "_http._tcp" {
					t.Errorf("Expected Name to be '_http._tcp', got %q", srv.Header().Name)
				}
			},
		},
		{
			name: "PTR record",
			input: &dns.PTR{
				Hdr: dns.RR_Header{
					Name:   "1.2.0.192.in-addr.arpa.",
					Rrtype: dns.TypePTR,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				Ptr: "www.example.com.",
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				ptr := rr.(*dns.PTR)
				if ptr.Ptr != "www" {
					t.Errorf("Expected Ptr to be 'www', got %q", ptr.Ptr)
				}
			},
		},
		{
			name: "SOA record",
			input: &dns.SOA{
				Hdr: dns.RR_Header{
					Name:   "example.com.",
					Rrtype: dns.TypeSOA,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				Ns:      "ns1.example.com.",
				Mbox:    "admin.example.com.",
				Serial:  2024010101,
				Refresh: 3600,
				Retry:   600,
				Expire:  604800,
				Minttl:  86400,
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				soa := rr.(*dns.SOA)
				if soa.Ns != "ns1" {
					t.Errorf("Expected Ns to be 'ns1', got %q", soa.Ns)
				}
				if soa.Mbox != "admin" {
					t.Errorf("Expected Mbox to be 'admin', got %q", soa.Mbox)
				}
			},
		},
		{
			name: "SOA record 2",
			input: &dns.SOA{
				Hdr: dns.RR_Header{
					Name:   "example.com.",
					Rrtype: dns.TypeSOA,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				Ns:      "ns1.",
				Mbox:    "hostmaster.",
				Serial:  2024010101,
				Refresh: 3600,
				Retry:   600,
				Expire:  604800,
				Minttl:  86400,
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				soa := rr.(*dns.SOA)
				if soa.Ns != "ns1." {
					t.Errorf("Expected Ns to be 'ns1.', got %q", soa.Ns)
				}
				if soa.Mbox != "hostmaster." {
					t.Errorf("Expected Mbox to be 'hostmaster.', got %q", soa.Mbox)
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := RRRelative(tt.input, tt.origin)
			if result == nil {
				t.Fatal("RRRelative returned nil")
			}
			tt.validate(t, result.(dns.RR))
		})
	}
}

func TestRRAbsolute(t *testing.T) {
	origin := "example.com."

	tests := []struct {
		name     string
		input    dns.RR
		origin   string
		validate func(t *testing.T, rr dns.RR)
	}{
		{
			name: "A record relative domain",
			input: &dns.A{
				Hdr: dns.RR_Header{
					Name:   "www",
					Rrtype: dns.TypeA,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				A: []byte{192, 0, 2, 1},
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				if rr.Header().Name != "www.example.com." {
					t.Errorf("Expected Name to be 'www.example.com.', got %q", rr.Header().Name)
				}
			},
		},
		{
			name: "NS record with relative Ns",
			input: &dns.NS{
				Hdr: dns.RR_Header{
					Name:   "@",
					Rrtype: dns.TypeNS,
					Class:  dns.ClassINET,
					Ttl:    86400,
				},
				Ns: "ns1",
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				ns := rr.(*dns.NS)
				if ns.Header().Name != "example.com." {
					t.Errorf("Expected Name to be 'example.com.', got %q", ns.Header().Name)
				}
				if ns.Ns != "ns1.example.com." {
					t.Errorf("Expected Ns to be 'ns1.example.com.', got %q", ns.Ns)
				}
			},
		},
		{
			name: "MX record with relative Mx",
			input: &dns.MX{
				Hdr: dns.RR_Header{
					Name:   "@",
					Rrtype: dns.TypeMX,
					Class:  dns.ClassINET,
					Ttl:    1800,
				},
				Preference: 10,
				Mx:         "mail",
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				mx := rr.(*dns.MX)
				if mx.Mx != "mail.example.com." {
					t.Errorf("Expected Mx to be 'mail.example.com.', got %q", mx.Mx)
				}
			},
		},
		{
			name: "CNAME record with relative Target",
			input: &dns.CNAME{
				Hdr: dns.RR_Header{
					Name:   "www",
					Rrtype: dns.TypeCNAME,
					Class:  dns.ClassINET,
					Ttl:    600,
				},
				Target: "target",
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				cname := rr.(*dns.CNAME)
				if cname.Target != "target.example.com." {
					t.Errorf("Expected Target to be 'target.example.com.', got %q", cname.Target)
				}
			},
		},
		{
			name: "SRV record with relative Target",
			input: &dns.SRV{
				Hdr: dns.RR_Header{
					Name:   "_http._tcp",
					Rrtype: dns.TypeSRV,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				Priority: 10,
				Weight:   60,
				Port:     80,
				Target:   "server",
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				srv := rr.(*dns.SRV)
				if srv.Target != "server.example.com." {
					t.Errorf("Expected Target to be 'server.example.com.', got %q", srv.Target)
				}
				if srv.Header().Name != "_http._tcp.example.com." {
					t.Errorf("Expected Name to be '_http._tcp.example.com.', got %q", srv.Header().Name)
				}
			},
		},
		{
			name: "PTR record with relative Ptr",
			input: &dns.PTR{
				Hdr: dns.RR_Header{
					Name:   "1.2.0.192.in-addr.arpa.",
					Rrtype: dns.TypePTR,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				Ptr: "www",
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				ptr := rr.(*dns.PTR)
				if ptr.Ptr != "www.example.com." {
					t.Errorf("Expected Ptr to be 'www.example.com.', got %q", ptr.Ptr)
				}
			},
		},
		{
			name: "SOA record with relative fields",
			input: &dns.SOA{
				Hdr: dns.RR_Header{
					Name:   "@",
					Rrtype: dns.TypeSOA,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				Ns:      "ns1",
				Mbox:    "admin",
				Serial:  2024010101,
				Refresh: 3600,
				Retry:   600,
				Expire:  604800,
				Minttl:  86400,
			},
			origin: origin,
			validate: func(t *testing.T, rr dns.RR) {
				soa := rr.(*dns.SOA)
				if soa.Ns != "ns1.example.com." {
					t.Errorf("Expected Ns to be 'ns1.example.com.', got %q", soa.Ns)
				}
				if soa.Mbox != "admin.example.com." {
					t.Errorf("Expected Mbox to be 'admin.example.com.', got %q", soa.Mbox)
				}
			},
		},
		{
			name: "empty origin",
			input: &dns.A{
				Hdr: dns.RR_Header{
					Name:   "www.example.com.",
					Rrtype: dns.TypeA,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				A: []byte{192, 0, 2, 1},
			},
			origin: "",
			validate: func(t *testing.T, rr dns.RR) {
				if rr.Header().Name != "www.example.com." {
					t.Errorf("Expected Name to remain 'www.example.com.', got %q", rr.Header().Name)
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := RRAbsolute(tt.input, tt.origin)
			if result == nil {
				t.Fatal("RRAbsolute returned nil")
			}
			tt.validate(t, result.(dns.RR))
		})
	}
}

func TestCopyRecord(t *testing.T) {
	tests := []struct {
		name     string
		input    dns.RR
		validate func(t *testing.T, original, copy dns.RR)
	}{
		{
			name: "A record",
			input: &dns.A{
				Hdr: dns.RR_Header{
					Name:   "www.example.com.",
					Rrtype: dns.TypeA,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				A: []byte{192, 0, 2, 1},
			},
			validate: func(t *testing.T, original, copy dns.RR) {
				origA := original.(*dns.A)
				copyA := copy.(*dns.A)
				if origA.A.String() != copyA.A.String() {
					t.Errorf("Expected IP to match, original: %s, copy: %s", origA.A.String(), copyA.A.String())
				}
				if origA.Header().Name != copyA.Header().Name {
					t.Errorf("Expected Name to match")
				}
				// Verify it's a deep copy by modifying the copy
				copyA.A = []byte{192, 0, 2, 2}
				if origA.A.String() == copyA.A.String() {
					t.Error("Copy is not deep - modifying copy affected original")
				}
			},
		},
		{
			name: "MX record",
			input: &dns.MX{
				Hdr: dns.RR_Header{
					Name:   "example.com.",
					Rrtype: dns.TypeMX,
					Class:  dns.ClassINET,
					Ttl:    1800,
				},
				Preference: 10,
				Mx:         "mail.example.com.",
			},
			validate: func(t *testing.T, original, copy dns.RR) {
				origMX := original.(*dns.MX)
				copyMX := copy.(*dns.MX)
				if origMX.Preference != copyMX.Preference {
					t.Errorf("Expected Preference to match, original: %d, copy: %d", origMX.Preference, copyMX.Preference)
				}
				if origMX.Mx != copyMX.Mx {
					t.Errorf("Expected Mx to match, original: %s, copy: %s", origMX.Mx, copyMX.Mx)
				}
				// Verify it's a deep copy
				copyMX.Mx = "mail2.example.com."
				if origMX.Mx == copyMX.Mx {
					t.Error("Copy is not deep - modifying copy affected original")
				}
			},
		},
		{
			name: "TXT record",
			input: &dns.TXT{
				Hdr: dns.RR_Header{
					Name:   "_dmarc.example.com.",
					Rrtype: dns.TypeTXT,
					Class:  dns.ClassINET,
					Ttl:    300,
				},
				Txt: []string{"v=DMARC1", "p=none"},
			},
			validate: func(t *testing.T, original, copy dns.RR) {
				origTXT := original.(*dns.TXT)
				copyTXT := copy.(*dns.TXT)
				if !reflect.DeepEqual(origTXT.Txt, copyTXT.Txt) {
					t.Errorf("Expected Txt to match, original: %v, copy: %v", origTXT.Txt, copyTXT.Txt)
				}
				// Verify it's a deep copy
				copyTXT.Txt[0] = "v=DMARC2"
				if origTXT.Txt[0] == copyTXT.Txt[0] {
					t.Error("Copy is not deep - modifying copy affected original")
				}
			},
		},
		{
			name: "SRV record",
			input: &dns.SRV{
				Hdr: dns.RR_Header{
					Name:   "_http._tcp.example.com.",
					Rrtype: dns.TypeSRV,
					Class:  dns.ClassINET,
					Ttl:    3600,
				},
				Priority: 10,
				Weight:   60,
				Port:     80,
				Target:   "server.example.com.",
			},
			validate: func(t *testing.T, original, copy dns.RR) {
				origSRV := original.(*dns.SRV)
				copySRV := copy.(*dns.SRV)
				if origSRV.Priority != copySRV.Priority ||
					origSRV.Weight != copySRV.Weight ||
					origSRV.Port != copySRV.Port ||
					origSRV.Target != copySRV.Target {
					t.Error("SRV record fields do not match")
				}
				// Verify it's a deep copy
				copySRV.Target = "server2.example.com."
				if origSRV.Target == copySRV.Target {
					t.Error("Copy is not deep - modifying copy affected original")
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := CopyRecord(tt.input)
			if result == nil {
				t.Fatal("CopyRecord returned nil")
			}
			if dnsrr, ok := result.(dns.RR); ok {
				tt.validate(t, tt.input, dnsrr)
			} else {
				t.Error("Result is not a dns.RR")
			}
		})
	}
}
