package retryabledns

import (
	"errors"
	"fmt"
	"net"
	"time"
)

var (
	ErrMaxRetriesZero = errors.New("retries must be at least 1")
	ErrResolversEmpty = errors.New("resolvers list must not be empty")

	BaseResolvers = []string{
		"1.1.1.1:53",
		"1.0.0.1:53",
		"8.8.8.8:53",
		"8.8.4.4:53",
	}
	DefaultOptions = Options{
		BaseResolvers: BaseResolvers,
		MaxRetries:    1,
		Timeout:       3 * time.Second,
	}
)

type Options struct {
	BaseResolvers         []string
	MaxRetries            int
	Timeout               time.Duration
	Hostsfile             bool
	LocalAddrIP           net.IP
	LocalAddrPort         uint16
	ConnectionPoolThreads int
	MaxPerCNAMEFollows    int
	Proxy                 string
}

// Returns a net.Addr of a UDP or TCP type depending on whats required
func (options *Options) GetLocalAddr(proto Protocol) net.Addr {
	if options.LocalAddrIP == nil {
		return nil
	}
	ipPort := net.JoinHostPort(options.LocalAddrIP.String(), fmt.Sprint(options.LocalAddrPort))
	var ipAddr net.Addr
	switch proto {
	case UDP:
		ipAddr, _ = net.ResolveUDPAddr("udp", ipPort)
	default:
		ipAddr, _ = net.ResolveTCPAddr("tcp", ipPort)
	}
	return ipAddr
}

// Sets the ip from a string, if invalid sets as nil
func (options *Options) SetLocalAddrIP(ip string) {
	// invalid ips are no-ops
	options.LocalAddrIP = net.ParseIP(ip)
}

// Sets the first available IP from a network interface name e.g. eth0
func (options *Options) SetLocalAddrIPFromNetInterface(ifaceName string) error {
	iface, err := net.InterfaceByName(ifaceName)
	if err != nil {
		return err
	}
	addrs, err := iface.Addrs()
	if err != nil {
		return err
	}
	for _, addr := range addrs {
		ipnetAddr, ok := addr.(*net.IPNet)
		if !ok {
			continue
		}
		options.LocalAddrIP = ipnetAddr.IP
		return nil
	}
	return errors.New("no ip address found for interface")
}

func (options *Options) Validate() error {
	if options.MaxRetries == 0 {
		return ErrMaxRetriesZero
	}

	if len(options.BaseResolvers) == 0 {
		return ErrResolversEmpty
	}
	return nil
}
