package config

import (
	"github.com/go-playground/validator/v10"
	"log"
	"net"
	"strings"
)

var v = validator.New()

func init() {
	if err := v.RegisterValidation("iface", func(fl validator.FieldLevel) bool {
		name, ok := fl.Field().Interface().(string)
		if ok && name != "" {
			ifaces, err := net.Interfaces()
			if err != nil {
				log.Printf("error getting interfaces: %v", err)
				return false
			}

			for _, i := range ifaces {
				if i.Name == name {
					return true
				}
			}
		}

		return false
	}); err != nil {
		panic(err)
	}
}

type Configuration struct {
	Host  Host
	Peers []Peer `validate:"dive"`
}

type Host struct {
	Crypto    string `validate:"required,oneof=None Blake2s"`
	SharedKey string `validate:"required_if=Crypto Blake2s"`
	MTU       uint   `validate:"required,min=576"`
}

type Peer struct {
	Method string `validate:"oneof=TCP UDP"`

	LocalHost string `validate:"omitempty,ip|iface"`
	LocalPort uint   `validate:"max=65535"`

	RemoteHost string `validate:"required_with=RemotePort,omitempty,fqdn|ip"`
	RemotePort uint   `validate:"required_with=RemoteHost,omitempty,max=65535"`

	Congestion string `validate:"required_unless=Method TCP,omitempty,oneof=NewReno None"`

	KeepAlive uint
	Timeout   uint
	RetryWait uint
}

func (p Peer) GetLocalHost() string {
	if p.LocalHost == "" {
		return ""
	}

	if err := v.Var(p.LocalHost, "ip"); err == nil {
		return p.LocalHost
	}

	iface, err := net.InterfaceByName(p.LocalHost)
	if err != nil {
		panic(err)
	}

	if iface != nil {
		addrs, err := iface.Addrs()
		if err != nil {
			panic(err)
		}

		if len(addrs) > 0 {
			addr := addrs[0].String()
			addr = strings.Split(addr, "/")[0]
			log.Printf("resolved interface `%s` to `%v`", p.LocalHost, addr)
			return addr
		}
	}

	return "invalid"
}

func (c Configuration) Validate() error {
	return v.Struct(c)
}