package wgsd

import (
	"fmt"
	"net"
	"strconv"

	"github.com/coredns/caddy"
	"github.com/coredns/coredns/core/dnsserver"
	"github.com/coredns/coredns/plugin"
	"github.com/miekg/dns"
	"golang.zx2c4.com/wireguard/wgctrl"
)

func init() {
	plugin.Register(pluginName, setup)
}

const (
	optionSelfAllowedIPs = "self-allowed-ips"
	optionSelfEndpoint   = "self-endpoint"
)

func parse(c *caddy.Controller) (*WGSD, error) {
	p := &WGSD{}
	for c.Next() {
		args := c.RemainingArgs()
		if len(args) != 2 {
			return nil, fmt.Errorf("expected 2 args, got %d", len(args))
		}
		p.zone = dns.Fqdn(args[0])
		p.device = args[1]

		for c.NextBlock() {
			switch c.Val() {
			case optionSelfAllowedIPs:
				p.selfAllowedIPs = make([]net.IPNet, 0)
				for _, aip := range c.RemainingArgs() {
					_, prefix, err := net.ParseCIDR(aip)
					if err != nil {
						return nil, fmt.Errorf("invalid self-allowed-ips: %s err: %v", c.Val(), err)
					}
					p.selfAllowedIPs = append(p.selfAllowedIPs, *prefix)
				}
			case optionSelfEndpoint:
				endpoint := c.RemainingArgs()
				if len(endpoint) != 1 {
					return nil, fmt.Errorf("expected 1 arg, got %d", len(endpoint))
				}
				host, portS, err := net.SplitHostPort(endpoint[0])
				if err != nil {
					return nil, fmt.Errorf("invalid self-endpoint, err: %v", err)
				}
				port, err := strconv.Atoi(portS)
				if err != nil {
					return nil, fmt.Errorf("error converting self-endpoint port: %v", err)
				}
				ip := net.ParseIP(host)
				if ip == nil {
					return nil, fmt.Errorf("invalid self-endpoint, invalid IP address: %s", host)
				}
				p.selfEndpoint = &net.UDPAddr{
					IP:   ip,
					Port: port,
				}
			default:
				return nil, c.ArgErr()
			}
		}
	}

	return p, nil
}

func setup(c *caddy.Controller) error {
	wgsd, err := parse(c)
	if err != nil {
		return plugin.Error(pluginName, err)
	}
	client, err := wgctrl.New()
	if err != nil {
		return plugin.Error(pluginName,
			fmt.Errorf("error constructing wgctrl client: %v",
				err))
	}
	c.OnFinalShutdown(client.Close)
	wgsd.client = client

	// Add the Plugin to CoreDNS, so Servers can use it in their plugin chain.
	dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
		wgsd.Next = next
		return wgsd
	})

	return nil
}