package server

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

	"coopcloud.tech/abra/cli/internal"
	"coopcloud.tech/abra/pkg/client"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/swarm"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli/v2"
)

var serverInitCommand = &cli.Command{
	Name:      "init",
	Usage:     "Initialise server for deploying apps",
	Aliases:   []string{"i"},
	HideHelp:  true,
	ArgsUsage: "<domain>",
	Description: `
Initialise swarm mode on the target <domain>.

This initialisation explicitly chooses the "single host swarm" mode which uses
the default IPv4 address as the advertising address. This can be re-configured
later for more advanced use cases.
`,
	Action: func(c *cli.Context) error {
		domainName := internal.ValidateDomain(c)

		cl, err := client.New(domainName)
		if err != nil {
			return err
		}

		// 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),
				}
				// comrade librehosters DNS resolver https://snopyta.org/service/dns/
				return d.DialContext(ctx, "udp", freifunkDNS)
			},
		}
		logrus.Debugf("created DNS resolver via '%s'", freifunkDNS)

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

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

		initReq := swarm.InitRequest{
			ListenAddr:    "0.0.0.0:2377",
			AdvertiseAddr: ipv4,
		}
		if _, err := cl.SwarmInit(ctx, initReq); err != nil {
			return err
		}
		logrus.Debugf("initialised swarm on '%s'", domainName)

		netOpts := types.NetworkCreate{Driver: "overlay", Scope: "swarm"}
		if _, err := cl.NetworkCreate(ctx, "proxy", netOpts); err != nil {
			return err
		}
		logrus.Debug("swarm overlay network 'proxy' created")

		return nil
	},
}