package ddwrt

import (
	"errors"
	"fmt"
	"os/exec"

	"github.com/kardianos/service"

	"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"
)

const Name = "ddwrt"

//lint:ignore ST1005 This error is for human.
var errDdwrtJffs2NotEnabled = errors.New(`could not install service without jffs, follow this guide to enable:

https://wiki.dd-wrt.com/wiki/index.php/Journalling_Flash_File_System
`)

var nvramKvMap = map[string]string{
	"dns_dnsmasq":     "1", // Make dnsmasq running but disable DNS ability, ctrld will replace it.
	"dnsmasq_options": "",  // Configuration of dnsmasq set by ctrld, filled by setupDDWrt.
	"dns_crypt":       "0", // Disable DNSCrypt.
	"dnssec":          "0", // Disable DNSSEC.
}

type Ddwrt struct {
	cfg *ctrld.Config
}

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

func (d *Ddwrt) ConfigureService(config *service.Config) error {
	if !ddwrtJff2Enabled() {
		return errDdwrtJffs2NotEnabled
	}
	return nil
}

func (d *Ddwrt) Install(_ *service.Config) error {
	return nil
}

func (d *Ddwrt) Uninstall(_ *service.Config) error {
	return nil
}

func (d *Ddwrt) PreRun() error {
	_ = d.Cleanup()
	return ntp.WaitNvram()
}

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

	data, err := dnsmasq.ConfTmpl(dnsmasq.ConfigContentTmpl, d.cfg)
	if err != nil {
		return err
	}

	nvramKvMap["dnsmasq_options"] = data
	if err := nvram.SetKV(nvramKvMap, nvram.CtrldSetupKey); err != nil {
		return err
	}

	// Restart dnsmasq service.
	if err := restartDNSMasq(); err != nil {
		return err
	}
	return nil
}

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

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

	// Restart dnsmasq service.
	if err := restartDNSMasq(); err != nil {
		return err
	}
	return nil
}

func restartDNSMasq() error {
	if out, err := exec.Command("restart_dns").CombinedOutput(); err != nil {
		return fmt.Errorf("restart_dns: %s, %w", string(out), err)
	}
	return nil
}

func ddwrtJff2Enabled() bool {
	out, _ := nvram.Run("get", "enable_jffs2")
	return out == "1"
}
