package dhcpsvc

import (
	"fmt"
	"time"

	"github.com/AdguardTeam/golibs/netutil"
	"golang.org/x/exp/maps"
	"golang.org/x/exp/slices"
)

// Config is the configuration for the DHCP service.
type Config struct {
	// Interfaces stores configurations of DHCP server specific for the network
	// interface identified by its name.
	Interfaces map[string]*InterfaceConfig

	// LocalDomainName is the top-level domain name to use for resolving DHCP
	// clients' hostnames.
	LocalDomainName string

	// ICMPTimeout is the timeout for checking another DHCP server's presence.
	ICMPTimeout time.Duration

	// Enabled is the state of the service, whether it is enabled or not.
	Enabled bool
}

// InterfaceConfig is the configuration of a single DHCP interface.
type InterfaceConfig struct {
	// IPv4 is the configuration of DHCP protocol for IPv4.
	IPv4 *IPv4Config

	// IPv6 is the configuration of DHCP protocol for IPv6.
	IPv6 *IPv6Config
}

// Validate returns an error in conf if any.
func (conf *Config) Validate() (err error) {
	switch {
	case conf == nil:
		return errNilConfig
	case !conf.Enabled:
		return nil
	case conf.ICMPTimeout < 0:
		return newMustErr("icmp timeout", "be non-negative", conf.ICMPTimeout)
	}

	err = netutil.ValidateDomainName(conf.LocalDomainName)
	if err != nil {
		// Don't wrap the error since it's informative enough as is.
		return err
	}

	if len(conf.Interfaces) == 0 {
		return errNoInterfaces
	}

	ifaces := maps.Keys(conf.Interfaces)
	slices.Sort(ifaces)

	for _, iface := range ifaces {
		if err = conf.Interfaces[iface].validate(); err != nil {
			return fmt.Errorf("interface %q: %w", iface, err)
		}
	}

	return nil
}

// newMustErr returns an error that indicates that valName must be as must
// describes.
func newMustErr(valName, must string, val fmt.Stringer) (err error) {
	return fmt.Errorf("%s %s must %s", valName, val, must)
}

// validate returns an error in ic, if any.
func (ic *InterfaceConfig) validate() (err error) {
	if ic == nil {
		return errNilConfig
	}

	if err = ic.IPv4.validate(); err != nil {
		return fmt.Errorf("ipv4: %w", err)
	}

	if err = ic.IPv6.validate(); err != nil {
		return fmt.Errorf("ipv6: %w", err)
	}

	return nil
}