package main

import (
	"os"
	"strings"
)

// initParams carries the init-server form fields from the web wizard. Empty
// fields fall back to the same defaults the interactive CLI prompt offers.
type initParams struct {
	Interface string `json:"interface"`
	Network   string `json:"network"`
	Port      string `json:"port"`
	PublicIP  string `json:"public_ip"`
	DNS       string `json:"dns"`
	MTU       string `json:"mtu"`
}

// initServerWeb is the non-interactive twin of initServer used by the web
// wizard. It performs the same steps — verify deps flag, generate keys and
// obfuscation parameters, write configs, enable forwarding, start the service —
// but takes its inputs from a form instead of stdin prompts.
func initServerWeb(p initParams) *Config {
	osInfo := detectOS()

	st := loadState()
	if !st.DepsInstalled {
		die("Dependencies not installed — run install-deps first")
	}
	requireBinary("awg")

	c := &Config{}
	c.Interface = valueOr(strings.TrimSpace(p.Interface), "awg0")
	c.Network = valueOr(strings.TrimSpace(p.Network), "10.0.0.0/24")
	requireSlash24(c.Network)
	c.Port = valueOr(strings.TrimSpace(p.Port), "51820")
	c.PublicIP = strings.TrimSpace(p.PublicIP)
	if c.PublicIP == "" {
		c.PublicIP = detectPublicIP()
	}
	c.DNS = valueOr(strings.TrimSpace(p.DNS), "1.1.1.1")
	c.MTU = valueOr(strings.TrimSpace(p.MTU), "1420")

	mtu := atoiOrDie(c.MTU)

	// Obfuscation parameters — same ceilings/constraints as initServer:
	//   Jmin < Jmax <= MTU ;  S1 <= MTU-148 ;  S2 <= MTU-92 ;  S1+56 != S2.
	mtuJunkCeil := mtu
	mtuS1Ceil := mtu - 148
	mtuS2Ceil := mtu - 92

	rndJc := randRange(4, 12)
	rndJmin := randRange(8, minInt(mtuJunkCeil-2, 32))
	rndJmax := randRange(maxInt(rndJmin+32, 80), minInt(mtuJunkCeil, 200))
	rndS1 := randRange(15, minInt(mtuS1Ceil, 150))
	rndS2 := randRange(15, minInt(mtuS2Ceil, 150))
	for rndS1+56 == rndS2 {
		rndS2 = randRange(15, minInt(mtuS2Ceil, 150))
	}

	c.Jc = itoa(rndJc)
	c.Jmin = itoa(rndJmin)
	c.Jmax = itoa(rndJmax)
	c.S1 = itoa(rndS1)
	c.S2 = itoa(rndS2)

	// Four distinct magic-header values in [5, 2^31-1].
	h1 := randMagic()
	h2 := randMagic()
	for h2 == h1 {
		h2 = randMagic()
	}
	h3 := randMagic()
	for h3 == h1 || h3 == h2 {
		h3 = randMagic()
	}
	h4 := randMagic()
	for h4 == h1 || h4 == h2 || h4 == h3 {
		h4 = randMagic()
	}
	c.H1 = itoa64(h1)
	c.H2 = itoa64(h2)
	c.H3 = itoa64(h3)
	c.H4 = itoa64(h4)

	// Server keys.
	priv, err := output("awg", "genkey")
	if err != nil {
		die("awg genkey failed: %v", err)
	}
	pub, err := outputWithInput(priv, "awg", "pubkey")
	if err != nil {
		die("awg pubkey failed: %v", err)
	}
	c.ServerPriv = priv
	c.ServerPub = pub

	srvIP := serverIP(c.Network)
	defIface := defaultRouteIface()

	writeConfig(c)

	confPath := awgConfPath(c.Interface)
	if err := os.MkdirAll(awgConfDir, 0755); err != nil {
		die("Failed to create %s: %v", awgConfDir, err)
	}
	if err := os.WriteFile(confPath, []byte(awgConfHeader(c, c.ServerPriv, srvIP, c.Port, c.MTU)), 0600); err != nil {
		die("Failed to write %s: %v", confPath, err)
	}
	writeNftRules(c.Interface, defIface, c.MTU)

	// IP forwarding (persistent).
	enableIPForwarding()

	serviceEnable(osInfo, c.Interface)
	serviceStart(osInfo, c.Interface)

	initStorage()
	info("Server initialised via web: interface=%s pubkey=%s", c.Interface, c.ServerPub)
	return c
}

// syncConfigWeb is the non-interactive twin of syncConfig: it rebuilds the
// interface conf from the ACTIVE peers in the registry and optionally restarts
// the service, without the CLI's stdin confirmation prompt.
func syncConfigWeb(c *Config, osInfo *OSInfo, restart bool) {
	confPath := awgConfPath(c.Interface)
	if _, err := os.Stat(confPath); err != nil {
		die("%s not found — run init-server first", confPath)
	}

	srvIP := serverIP(c.Network)
	defIface := defaultRouteIface()
	mtu := c.mtuOr("1420")

	clients := loadRegistry()
	var b strings.Builder
	b.WriteString(awgConfHeader(c, c.ServerPriv, srvIP, c.Port, mtu))
	for _, cl := range clients {
		if cl.IsEnabled == "ACTIVE" {
			b.WriteString(peerBlock(cl.Name, cl.PublicKey, cl.PSKKey, cl.IP))
		}
	}
	if err := os.WriteFile(confPath, []byte(b.String()), 0600); err != nil {
		die("Failed to write %s: %v", confPath, err)
	}
	writeNftRules(c.Interface, defIface, mtu)

	if restart {
		serviceRestart(osInfo, c.Interface)
	}
}
