package tomato

import (
	"fmt"
	"os/exec"

	"github.com/Control-D-Inc/ctrld"
	"github.com/Control-D-Inc/ctrld/internal/router/dnsmasq"
	"github.com/Control-D-Inc/ctrld/internal/router/ntp"
	"github.com/Control-D-Inc/ctrld/internal/router/nvram"
	"github.com/kardianos/service"
)

const (
	Name = "freshtomato"

	tomatoDnsCryptProxySvcName = "dnscrypt-proxy"
	tomatoStubbySvcName        = "stubby"
	tomatoDNSMasqSvcName       = "dnsmasq"
)

var nvramKvMap = map[string]string{
	"dnsmasq_custom": "",  // Configuration of dnsmasq set by ctrld, filled by setupTomato.
	"dnscrypt_proxy": "0", // Disable DNSCrypt.
	"dnssec_enable":  "0", // Disable DNSSEC.
	"stubby_proxy":   "0", // Disable Stubby
}

type FreshTomato struct {
	cfg *ctrld.Config
}

// New returns a router.Router for configuring/setup/run ctrld on Ubios routers.
func New(cfg *ctrld.Config) *FreshTomato {
	return &FreshTomato{cfg: cfg}
}

func (f *FreshTomato) ConfigureService(config *service.Config) error {
	return nil
}

func (f *FreshTomato) Install(_ *service.Config) error {
	return nil
}

func (f *FreshTomato) Uninstall(_ *service.Config) error {
	return nil
}

func (f *FreshTomato) PreRun() error {
	_ = f.Cleanup()
	return ntp.WaitNvram()
}

func (f *FreshTomato) Setup() error {
	if f.cfg.FirstListener().IsDirectDnsListener() {
		return nil
	}
	// Already setup.
	if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" {
		return nil
	}

	data, err := dnsmasq.ConfTmpl(dnsmasq.ConfigContentTmpl, f.cfg)
	if err != nil {
		return err
	}
	nvramKvMap["dnsmasq_custom"] = data
	if err := nvram.SetKV(nvramKvMap, nvram.CtrldSetupKey); err != nil {
		return err
	}

	// Restart dnscrypt-proxy service.
	if err := tomatoRestartServiceWithKill(tomatoDnsCryptProxySvcName, true); err != nil {
		return err
	}
	// Restart stubby service.
	if err := tomatoRestartService(tomatoStubbySvcName); err != nil {
		return err
	}
	// Restart dnsmasq service.
	if err := restartDNSMasq(); err != nil {
		return err
	}
	return nil
}

func (f *FreshTomato) Cleanup() error {
	if f.cfg.FirstListener().IsDirectDnsListener() {
		return nil
	}
	if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val != "1" {
		return nil // was restored, nothing to do.
	}

	nvramKvMap["dnsmasq_custom"] = ""
	// Restore old configs.
	if err := nvram.Restore(nvramKvMap, nvram.CtrldSetupKey); err != nil {
		return err
	}

	// Restart dnscrypt-proxy service.
	if err := tomatoRestartServiceWithKill(tomatoDnsCryptProxySvcName, true); err != nil {
		return err
	}
	// Restart stubby service.
	if err := tomatoRestartService(tomatoStubbySvcName); err != nil {
		return err
	}
	// Restart dnsmasq service.
	if err := restartDNSMasq(); err != nil {
		return err
	}
	return nil
}

func tomatoRestartService(name string) error {
	return tomatoRestartServiceWithKill(name, false)
}

func tomatoRestartServiceWithKill(name string, killBeforeRestart bool) error {
	if killBeforeRestart {
		_, _ = exec.Command("killall", name).CombinedOutput()
	}
	if out, err := exec.Command("service", name, "restart").CombinedOutput(); err != nil {
		return fmt.Errorf("service restart %s: %s, %w", name, string(out), err)
	}
	return nil
}

func restartDNSMasq() error {
	return tomatoRestartService(tomatoDNSMasqSvcName)
}
