package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

// initServer runs the interactive server setup. Unlike the original script it
// no longer installs dependencies — that is a separate, prerequisite step. It
// verifies the deps-installed flag recorded by `install-deps` and aborts if it
// is missing, then generates keys/obfuscation params, writes configs, enables
// forwarding and starts the service.
func initServer() {
	osInfo := detectOS()

	// New flow: dependency installation must have happened first.
	st := loadState()
	if !st.DepsInstalled {
		die("Dependencies not installed — run 'install-deps' first, then re-run 'init-server'")
	}
	info("Dependency check passed (installed %s on %s %s)",
		valueOr(st.InstalledAt, "unknown time"),
		valueOr(st.OSID, "?"), valueOr(st.OSVersion, "?"))
	// The awg binary must exist now that deps are installed.
	requireBinary("awg")

	fmt.Println()
	fmt.Println("=== AmneziaWG Server Initialization ===")
	fmt.Println()
	fmt.Println("Dependencies already installed. This will:")
	fmt.Println("  1. Generate server key pair")
	fmt.Println("  2. Generate AmneziaWG obfuscation parameters")
	fmt.Printf("  3. Write %s/<interface>.conf\n", awgConfDir)
	fmt.Println("  4. Enable IP forwarding (persistent)")
	fmt.Println("  5. Start and enable the AmneziaWG service")
	fmt.Println()
	if !confirm("Proceed?") {
		info("Aborted")
		os.Exit(0)
	}
	fmt.Println()

	c := &Config{}

	// Interactive configuration.
	c.Interface = promptDefault("AmneziaWG interface name", "awg0")
	c.Network = promptDefault("VPN network CIDR", "10.0.0.0/24")
	requireSlash24(c.Network)
	c.Port = promptDefault("Listen port", "51820")

	detectedIP := detectPublicIP()
	c.PublicIP = promptDefault("Server public IP or hostname", detectedIP)

	c.DNS = promptDefault("DNS server for clients", "1.1.1.1")
	c.MTU = promptDefault("MTU", "1420")

	mtu := atoiOrDie(c.MTU)

	// AmneziaWG obfuscation parameters — randomised defaults, editable.
	// Ceilings are derived from the interface MTU per the AmneziaWG spec:
	//   Jmin < Jmax <= MTU ;  S1 <= MTU-148 ;  S2 <= MTU-92
	mtuJunkCeil := mtu
	mtuS1Ceil := mtu - 148
	mtuS2Ceil := mtu - 92

	rndJc := randRange(4, 12)

	jminHi := minInt(mtuJunkCeil-2, 32)
	rndJmin := randRange(8, jminHi)

	jmaxLo := maxInt(rndJmin+32, 80)
	jmaxHi := minInt(mtuJunkCeil, 200)
	rndJmax := randRange(jmaxLo, jmaxHi)

	rndS1 := randRange(15, minInt(mtuS1Ceil, 150))
	rndS2 := randRange(15, minInt(mtuS2Ceil, 150))
	// S1 + 56 != S2 (spec constraint) — resample S2 until it holds.
	for rndS1+56 == rndS2 {
		rndS2 = randRange(15, minInt(mtuS2Ceil, 150))
	}

	fmt.Println()
	fmt.Println("AmneziaWG obfuscation parameters (shared by server and all clients):")
	c.Jc = promptDefault("Jc (junk packet count)", itoa(rndJc))
	c.Jmin = promptDefault("Jmin (min junk size)", itoa(rndJmin))
	c.Jmax = promptDefault("Jmax (max junk size)", itoa(rndJmax))
	c.S1 = promptDefault("S1 (init junk size)", itoa(rndS1))
	c.S2 = promptDefault("S2 (response junk size)", 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 = promptDefault("H1 (magic header 1)", itoa64(h1))
	c.H2 = promptDefault("H2 (magic header 2)", itoa64(h2))
	c.H3 = promptDefault("H3 (magic header 3)", itoa64(h3))
	c.H4 = promptDefault("H4 (magic header 4)", itoa64(h4))
	fmt.Println()

	// Generate server keys (awg is guaranteed present by the deps check).
	info("Generating 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()

	// Write the profiler config file.
	writeConfig(c)

	// Write <conf-dir>/<iface>.conf and companion nft ruleset.
	confPath := awgConfPath(c.Interface)
	info("Writing %s...", confPath)
	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.
	info("Enabling IP forwarding...")
	enableIPForwarding()

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

	// Init client storage.
	initStorage()

	fmt.Println()
	info("=== Server initialization complete ===")
	info("Interface : %s", c.Interface)
	info("Server IP : %s", srvIP)
	info("Network   : %s", c.Network)
	info("Port      : %s", c.Port)
	info("Public IP : %s", c.PublicIP)
	info("Public Key: %s", c.ServerPub)
	info("Obfusc.   : Jc=%s Jmin=%s Jmax=%s S1=%s S2=%s", c.Jc, c.Jmin, c.Jmax, c.S1, c.S2)
	fmt.Println()
	info("Next steps: use 'create <name>' to add VPN clients")
}

// ─── server management ───────────────────────────────────────────────────────────

func serverStatus(c *Config, osInfo *OSInfo) {
	fmt.Println()
	fmt.Println("=== AmneziaWG Server Configuration ===")
	fmt.Printf("  %-12s %s\n", "Interface:", c.Interface)
	fmt.Printf("  %-12s %s\n", "Network:", c.Network)
	fmt.Printf("  %-12s %s\n", "Port:", c.Port)
	fmt.Printf("  %-12s %s\n", "Public IP:", c.PublicIP)
	fmt.Printf("  %-12s %s\n", "Public Key:", c.ServerPub)
	fmt.Println()
	fmt.Println("=== Interface Status ===")
	if awgIfaceUp(c.Interface) {
		run("awg", "show", c.Interface)
	} else {
		fmt.Printf("  Interface %s is DOWN\n", c.Interface)
	}
	fmt.Println()
	fmt.Println("=== Service Status ===")
	serviceStatus(osInfo, c.Interface)
}

func showConfig() {
	c := loadConfig()
	confPath := awgConfPath(c.Interface)

	fmt.Println()
	fmt.Printf("=== Profiler Config (%s) ===\n", configFile)
	printFilteredConfig(configFile)
	fmt.Println()

	if data, err := os.ReadFile(confPath); err == nil {
		fmt.Printf("=== AmneziaWG Config (%s) ===\n", confPath)
		fmt.Print(hidePrivateKey(string(data)))
	} else {
		warn("%s not found", confPath)
	}
}

// printFilteredConfig reproduces the bash grep chain: drop lines containing
// PRIVATE / PASSW / KEY, comment lines, and blank lines.
func printFilteredConfig(path string) {
	f, err := os.Open(path)
	if err != nil {
		return
	}
	defer f.Close()
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, "PRIVATE") ||
			strings.Contains(line, "PASSW") ||
			strings.Contains(line, "KEY") {
			continue
		}
		if strings.HasPrefix(line, "#") || strings.TrimSpace(line) == "" {
			continue
		}
		fmt.Println(line)
	}
}

// hidePrivateKey masks the PrivateKey value (bash: sed s/PrivateKey.../<hidden>/).
func hidePrivateKey(content string) string {
	lines := strings.Split(content, "\n")
	for i, line := range lines {
		trimmed := strings.TrimSpace(line)
		if strings.HasPrefix(trimmed, "PrivateKey") {
			if idx := strings.Index(line, "="); idx >= 0 {
				lines[i] = line[:idx+1] + " <hidden>"
			}
		}
	}
	return strings.Join(lines, "\n")
}

// syncConfig regenerates the interface conf with all ACTIVE peers, optionally
// restarting the service.
func syncConfig(c *Config, osInfo *OSInfo) {
	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")

	info("Regenerating %s with all ACTIVE peers...", confPath)

	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)
	info("Config written: %s", confPath)

	if confirm("Restart AmneziaWG to apply changes?") {
		serviceRestart(osInfo, c.Interface)
		info("AmneziaWG restarted")
	}
}
