package internal

import (
	"context"
	"fmt"
	"log/slog"
	"net"
	"slices"
	"strings"

	"github.com/go-acme/lego/v5/challenge"
	"github.com/go-acme/lego/v5/log"
	"github.com/miekg/dns"
)

const DefaultResolvConf = "/etc/resolv.conf"

// GetRecursiveNameservers returns a copy of the recursive nameservers.
func (c *Client) GetRecursiveNameservers() []string {
	return slices.Clone(c.recursiveNameservers)
}

// LookupAuthoritativeNameservers returns the authoritative nameservers for the given fqdn.
func (c *Client) LookupAuthoritativeNameservers(ctx context.Context, fqdn string) ([]string, error) {
	var authoritativeNss []string

	zone, err := c.FindZoneByFqdn(ctx, fqdn)
	if err != nil {
		return nil, fmt.Errorf("could not find zone: %w", err)
	}

	r, err := c.SendQuery(ctx, zone, dns.TypeNS, true)
	if err != nil {
		return nil, fmt.Errorf("NS call failed: %w", err)
	}

	for _, rr := range r.Answer {
		if ns, ok := rr.(*dns.NS); ok {
			authoritativeNss = append(authoritativeNss, strings.ToLower(ns.Ns))
		}
	}

	if len(authoritativeNss) > 0 {
		return authoritativeNss, nil
	}

	return nil, fmt.Errorf("[zone=%s] could not determine authoritative nameservers", zone)
}

// getNameservers attempts to get systems nameservers before falling back to the defaults.
func getNameservers(path string, stack challenge.NetworkStack) []string {
	config, err := dns.ClientConfigFromFile(path)
	if err == nil && len(config.Servers) > 0 {
		return config.Servers
	}

	switch stack {
	case challenge.IPv4Only:
		return []string{
			"1.1.1.1:53",
			"1.0.0.1:53",
		}

	case challenge.IPv6Only:
		return []string{
			"[2606:4700:4700::1111]:53",
			"[2606:4700:4700::1001]:53",
		}

	default:
		return []string{
			"1.1.1.1:53",
			"1.0.0.1:53",
			"[2606:4700:4700::1111]:53",
			"[2606:4700:4700::1001]:53",
		}
	}
}

func parseNameservers(servers []string) []string {
	var resolvers []string

	for _, resolver := range servers {
		// ensure all servers have a port number
		if _, _, err := net.SplitHostPort(resolver); err != nil {
			log.Debug("The nameserver does not contain a port, assuming port 53", slog.String("nameserver", resolver), log.ErrorAttr(err))

			resolvers = append(resolvers, net.JoinHostPort(resolver, "53"))
		} else {
			resolvers = append(resolvers, resolver)
		}
	}

	return resolvers
}
