package dns

import (
	"context"
	"fmt"
	"net"
	"os"
	"time"

	"github.com/AlecAivazis/survey/v2"
	"github.com/sirupsen/logrus"
)

// NewToken constructs a new DNS provider token.
func NewToken(provider, providerTokenEnvVar string) (string, error) {
	if token, present := os.LookupEnv(providerTokenEnvVar); present {
		return token, nil
	}

	logrus.Debugf("no %s in environment, asking via stdin", providerTokenEnvVar)

	var token string
	prompt := &survey.Input{
		Message: fmt.Sprintf("%s API token?", provider),
	}
	if err := survey.AskOne(prompt, &token); err != nil {
		return "", err
	}

	return token, nil
}

// EnsureIPv4 ensures that an ipv4 address is set for a domain name
func EnsureIPv4(domainName string) (string, error) {
	var ipv4 string

	// comrade librehosters DNS resolver -> https://www.privacy-handbuch.de/handbuch_93d.htm
	freifunkDNS := "5.1.66.255:53"

	resolver := &net.Resolver{
		PreferGo: false,
		Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
			d := net.Dialer{
				Timeout: time.Millisecond * time.Duration(10000),
			}
			return d.DialContext(ctx, "udp", freifunkDNS)
		},
	}

	ctx := context.Background()
	ips, err := resolver.LookupIPAddr(ctx, domainName)
	if err != nil {
		return ipv4, err
	}

	if len(ips) == 0 {
		return ipv4, fmt.Errorf("unable to retrieve ipv4 address for %s", domainName)
	}

	ipv4 = ips[0].IP.To4().String()
	logrus.Debugf("%s points to %s (resolver: %s)", domainName, ipv4, freifunkDNS)

	return ipv4, nil
}

// EnsureDomainsResolveSameIPv4 ensures that domains resolve to the same ipv4 address
func EnsureDomainsResolveSameIPv4(domainName, server string) (string, error) {
	var ipv4 string

	domainIPv4, err := EnsureIPv4(domainName)
	if err != nil {
		return ipv4, err
	}

	if domainIPv4 == "" {
		return ipv4, fmt.Errorf("cannot resolve ipv4 for %s?", domainName)
	}

	serverIPv4, err := EnsureIPv4(server)
	if err != nil {
		return ipv4, err
	}

	if serverIPv4 == "" {
		return ipv4, fmt.Errorf("cannot resolve ipv4 for %s?", server)
	}

	if domainIPv4 != serverIPv4 {
		err := "app domain %s (%s) does not appear to resolve to app server %s (%s)?"
		return ipv4, fmt.Errorf(err, domainName, domainIPv4, server, serverIPv4)
	}

	return ipv4, nil
}

// GetTTL parses a ttl string into a duration
func GetTTL(ttl string) (time.Duration, error) {
	val, err := time.ParseDuration(ttl)
	if err != nil {
		return val, err
	}
	return val, nil
}