package resolver

import (
	"context"
	"fmt"
	"net"
	"strings"
	"time"
)

// UDPQuery sends a raw DNS query to addr over UDP and returns the raw response bytes.
//
// addr may be "ip" or "ip:port" — ":53" is appended if no port is specified.
// timeout sets the per-query deadline for the read/write operations.
// ctx allows early cancellation; if the context already has a deadline that
// is sooner than timeout, the context deadline takes precedence.
func UDPQuery(ctx context.Context, addr string, query []byte, timeout time.Duration) ([]byte, error) {
	if !strings.Contains(addr, ":") {
		addr = addr + ":53"
	}

	dialer := net.Dialer{Timeout: timeout}
	conn, err := dialer.DialContext(ctx, "udp", addr)
	if err != nil {
		return nil, fmt.Errorf("connecting to %s: %w", addr, err)
	}
	defer conn.Close()

	// Determine the effective deadline: use ctx deadline if sooner than timeout.
	deadline := time.Now().Add(timeout)
	if ctxDeadline, ok := ctx.Deadline(); ok && ctxDeadline.Before(deadline) {
		deadline = ctxDeadline
	}
	conn.SetDeadline(deadline) //nolint:errcheck

	if _, err := conn.Write(query); err != nil {
		return nil, fmt.Errorf("sending query to %s: %w", addr, err)
	}

	buf := make([]byte, udpBufSize)
	n, err := conn.Read(buf)
	if err != nil {
		return nil, fmt.Errorf("reading response from %s: %w", addr, err)
	}
	return buf[:n], nil
}
