package synology

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/nextdns/nextdns/config"
	"github.com/nextdns/nextdns/router/internal"
)

type Router struct {
	DNSMasqPath     string
	ListenPort      string
	ClientReporting bool
	CacheEnabled    bool
	PortZeroExists  bool

	disabled bool
}

func New() (*Router, bool) {
	if b, err := exec.Command("uname", "-u").Output(); err != nil ||
		!strings.HasPrefix(string(b), "synology") {
		return nil, false
	}
	return &Router{
		DNSMasqPath: "/etc/dhcpd/dhcpd-vendor-nextdns.conf", // SRM requires two dashes
		ListenPort:  "5342",
	}, true
}

func (r *Router) String() string {
	return "synology"
}

func (r *Router) Configure(c *config.Config) error {
	if b, err := os.ReadFile("/etc/dhcpd/dhcpd.info"); err != nil || !bytes.HasPrefix(b, []byte(`enable="yes"`)) {
		// DHCP is disabled, listen on 53 directly
		c.Listens = []string{":53"}
		r.disabled = true
		return nil
	}
	c.Listens = []string{"127.0.0.1:" + r.ListenPort}
	r.ClientReporting = c.ReportClientInfo
	if cs, _ := config.ParseBytes(c.CacheSize); cs > 0 {
		r.CacheEnabled = true
		c.Listens = []string{":53"}
		return r.setupDNSMasq() // Make dnsmasq stop listening on 53 before we do.
	}
	return nil
}

func (r *Router) Setup() error {
	if r.disabled || r.CacheEnabled {
		return nil
	}
	return r.setupDNSMasq()
}

func (r *Router) setupDNSMasq() error {
	if r.CacheEnabled {
		b, err := os.ReadFile("/etc/dhcpd/dhcpd.conf")
		r.PortZeroExists = err == nil && hasPortZero(b)
	}
	if err := internal.WriteTemplate(r.DNSMasqPath, tmpl, r, 0644); err != nil {
		return err
	}
	infoFile := strings.Replace(r.DNSMasqPath, ".conf", ".info", 1)
	if err := os.WriteFile(infoFile, []byte(`enable="yes"`), 0644); err != nil {
		return err
	}
	return restartDNSMasq()
}

func hasPortZero(b []byte) bool {
	for _, line := range bytes.Split(b, []byte{'\n'}) {
		if bytes.Equal(bytes.TrimSpace(line), []byte("port=0")) {
			return true
		}
	}
	return false
}

func (r *Router) Restore() error {
	if r.disabled {
		return nil
	}
	_ = os.Remove(r.DNSMasqPath)
	if err := restartDNSMasq(); err != nil {
		return fmt.Errorf("service restart_dnsmasq: %v", err)
	}
	return nil
}

func restartDNSMasq() error {
	// Restart dnsmasq.
	if err := exec.Command("/etc/rc.network", "nat-restart-dhcp").Run(); err != nil {
		return fmt.Errorf("/etc/rc.network nat-restart-dhcp: %v", err)
	}
	return nil
}

var tmpl = `# Configuration generated by NextDNS
{{- if .CacheEnabled}}
# DNS is handled by NextDNS
{{- if not .PortZeroExists}}
port=0
{{- end}}
{{- else}}
no-resolv
server=127.0.0.1#{{.ListenPort}}
{{- if .ClientReporting}}
add-mac
{{- end}}
add-subnet=32,128
{{- end}}
`
