package resolver_test

import (
	"context"
	"ekdns/resolver"
	"io"
	"testing"
	"time"

	"golang.org/x/net/dns/dnsmessage"
)

// ---------------------------------------------------------------------------
// ParallelAFallback tests (T013)
// ---------------------------------------------------------------------------

func TestParallelAFallback_FirstSuccessWins(t *testing.T) {
	qname := mustNewName("example.com.")

	// Server A: returns A records.
	addrA := startFakeDNSMulti(t, func(query []byte) []byte {
		return buildAResponse(queryID(query), qname, [][4]byte{{1, 2, 3, 4}})
	})
	// Server B: returns NXDOMAIN.
	addrB := startFakeDNSMulti(t, func(query []byte) []byte {
		return buildNXDOMAINResponse(queryID(query), qname)
	})

	ips, err := resolver.ParallelAFallback(context.Background(), io.Discard, []string{addrA, addrB}, "example.com", 2*time.Second)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(ips) == 0 {
		t.Error("expected at least one IP")
	}
}

func TestParallelAFallback_AllNXDOMAIN(t *testing.T) {
	qname := mustNewName("noexist.example.com.")

	addrA := startFakeDNSMulti(t, func(query []byte) []byte {
		return buildNXDOMAINResponse(queryID(query), qname)
	})
	addrB := startFakeDNSMulti(t, func(query []byte) []byte {
		return buildNXDOMAINResponse(queryID(query), qname)
	})

	_, err := resolver.ParallelAFallback(context.Background(), io.Discard, []string{addrA, addrB}, "noexist.example.com", 2*time.Second)
	if err == nil {
		t.Fatal("expected error for all-NXDOMAIN responses")
	}
}

func TestParallelAFallback_OneTimeout(t *testing.T) {
	qname := mustNewName("example.com.")

	silentAddr := startSilentDNS(t)
	responsiveAddr := startFakeDNSMulti(t, func(query []byte) []byte {
		return buildAResponse(queryID(query), qname, [][4]byte{{5, 6, 7, 8}})
	})

	ips, err := resolver.ParallelAFallback(
		context.Background(),
		io.Discard,
		[]string{silentAddr, responsiveAddr},
		"example.com",
		300*time.Millisecond,
	)
	if err != nil {
		t.Fatalf("expected success from responsive resolver, got error: %v", err)
	}
	if len(ips) == 0 {
		t.Error("expected IPs from responsive resolver")
	}
}

func TestParallelAFallback_AllFail(t *testing.T) {
	silentA := startSilentDNS(t)
	silentB := startSilentDNS(t)

	_, err := resolver.ParallelAFallback(
		context.Background(),
		io.Discard,
		[]string{silentA, silentB},
		"example.com",
		100*time.Millisecond,
	)
	if err == nil {
		t.Fatal("expected error when all resolvers fail")
	}
}

func TestParallelAFallback_EmptyResolvers(t *testing.T) {
	_, err := resolver.ParallelAFallback(context.Background(), io.Discard, nil, "example.com", time.Second)
	if err == nil {
		t.Fatal("expected error for empty resolver list")
	}
}

func TestParallelAFallback_CNAMEOnlyNoARecord(t *testing.T) {
	// Server returns a CNAME but no A record — queryA will return "no A records" error.
	// ParallelAFallback should return an error.
	qname := mustNewName("www.example.com.")
	cnameTarget := mustNewName("real.example.com.")

	addrA := startFakeDNSMulti(t, func(query []byte) []byte {
		return buildCNAMEResponse(queryID(query), qname, cnameTarget)
	})

	_, err := resolver.ParallelAFallback(context.Background(), io.Discard, []string{addrA}, "www.example.com", 2*time.Second)
	if err == nil {
		t.Error("expected error for CNAME-only response (no A records)")
	}
}

// ---------------------------------------------------------------------------
// Helper: build NS NXDOMAIN response (used in other test files)
// ---------------------------------------------------------------------------

func buildNSNXDOMAIN(id uint16, name dnsmessage.Name) []byte {
	msg := dnsmessage.Message{
		Header: dnsmessage.Header{
			ID:       id,
			Response: true,
			RCode:    dnsmessage.RCodeNameError,
		},
		Questions: []dnsmessage.Question{{
			Name:  name,
			Type:  dnsmessage.TypeNS,
			Class: dnsmessage.ClassINET,
		}},
	}
	packed, err := msg.Pack()
	if err != nil {
		panic("buildNSNXDOMAIN: pack failed: " + err.Error())
	}
	return packed
}
