// ash-agent: All Source Hosting host agent (proof of concept).
//
//	ash-agent vyos-config      render VyOS config offline (no Proxmox needed)
//	ash-agent preflight        read-only Proxmox checks
//	ash-agent deploy-vyos      create the VyOS router VM and write its config
//	ash-agent certify-request  produce the report a Netquor Engineer reviews
//
// Credentials come from the environment, never flags:
//
//	ASH_PVE_URL   https://pve.lan:8006
//	ASH_PVE_TOKEN user@pve!agent=<secret>
//	ASH_PVE_NODE  node name (default: pve)
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"path/filepath"
)

type deployOpts struct {
	VMID                    int
	Name                    string
	Cores, MemoryMB, DiskGB int
	Storage, ISO, CloudInit string
	WANBridge, LANBridge    string
}

func env(k, d string) string {
	if v := os.Getenv(k); v != "" {
		return v
	}
	return d
}

func logf(s string) { fmt.Println(s) }

func usage() {
	fmt.Fprintln(os.Stderr, "usage: ash-agent <vyos-config|preflight|deploy-vyos|certify-request> [flags]")
	os.Exit(2)
}

func main() {
	if len(os.Args) < 2 {
		usage()
	}
	cmd := os.Args[1]
	fs := flag.NewFlagSet(cmd, flag.ExitOnError)
	insecure := fs.Bool("insecure", false, "skip TLS verification (self-signed Proxmox cert)")
	dry := fs.Bool("dry-run", false, "print Proxmox calls instead of sending them")
	wan := fs.String("wan-bridge", "vmbr0", "Proxmox bridge connected to your ISP")
	lan := fs.String("lan-bridge", "vmbr1", "Proxmox bridge for tenant VMs (VLAN-aware)")
	out := fs.String("out", "./ash-out", "output directory")
	tenant := fs.String("tenant-cidr", "10.77.0.0/24", "tenant network behind the router")
	dns := fs.String("dns", "1.1.1.1", "DNS server handed to tenants")
	wg := fs.Bool("wireguard", false, "also configure a WireGuard endpoint (DIY connectivity)")
	vmid := fs.Int("vmid", 9100, "VMID for the VyOS VM")
	iso := fs.String("iso", "local:iso/vyos-1.4-generic-amd64.iso", "VyOS ISO volume id")
	storage := fs.String("storage", "local-lvm", "storage for the VyOS disk")
	snippet := fs.String("cloud-init-snippet", "", "storage:snippets/file.yaml to use VyOS cloud-init images")
	fs.Parse(os.Args[2:])

	pve := newPVE(env("ASH_PVE_URL", "https://localhost:8006"), os.Getenv("ASH_PVE_TOKEN"),
		env("ASH_PVE_NODE", "pve"), *insecure, *dry, logf)
	vo := vyosOpts{Hostname: "ash-vyos", WANIf: "eth0", LANIf: "eth1", TenantCIDR: *tenant,
		DNS: *dns, WireGuard: *wg, WGPort: 51820, WGCIDR: "10.99.0.1/24"}

	var err error
	switch cmd {
	case "vyos-config":
		err = writeConfig(vo, *out)
	case "preflight", "certify-request":
		if os.Getenv("ASH_PVE_TOKEN") == "" {
			err = fmt.Errorf("set ASH_PVE_TOKEN (user@pve!agent=<secret>)")
			break
		}
		var rep *certReport
		rep, err = buildReport(pve, certOpts{*wan, *lan})
		if err != nil {
			break
		}
		for _, c := range rep.Checks {
			mark := "PASS"
			if !c.Pass {
				mark = "FAIL"
			}
			fmt.Printf("[%s] %-24s %s\n", mark, c.Name, c.Detail)
		}
		if cmd == "certify-request" {
			b, _ := json.MarshalIndent(rep, "", "  ")
			p := filepath.Join(*out, "certify-request.json")
			os.MkdirAll(*out, 0o755)
			err = os.WriteFile(p, b, 0o644)
			fmt.Printf("\nrequest id: %s\nwrote %s\nSend this file to your Netquor Engineer. all_pass=%v\n", rep.RequestID, p, rep.AllPass)
		}
	case "deploy-vyos":
		if err = writeConfig(vo, *out); err != nil {
			break
		}
		if !*dry && os.Getenv("ASH_PVE_TOKEN") == "" {
			err = fmt.Errorf("set ASH_PVE_TOKEN or use --dry-run")
			break
		}
		err = pve.createVyOS(deployOpts{VMID: *vmid, Name: "vyos-router", Cores: 2, MemoryMB: 2048, DiskGB: 10,
			Storage: *storage, ISO: *iso, CloudInit: *snippet, WANBridge: *wan, LANBridge: *lan})
		if err == nil {
			fmt.Printf("\nVM %d created. Next: open its console, log in (vyos/vyos), run `install image`, reboot,\nthen paste %s. See docs.\n", *vmid, filepath.Join(*out, "vyos-bootstrap.sh"))
		}
	default:
		usage()
	}
	if err != nil {
		fmt.Fprintln(os.Stderr, "error:", err)
		os.Exit(1)
	}
}

func writeConfig(vo vyosOpts, dir string) error {
	res, err := vyosCommands(vo)
	if err != nil {
		return err
	}
	if err := os.MkdirAll(dir, 0o755); err != nil {
		return err
	}
	// 0600: the script contains the WireGuard private key when --wireguard is set.
	files := map[string]string{
		"vyos-bootstrap.sh": vbashScript(res.Commands),
		"user-data.yaml":    cloudInitUserData(res.Commands),
	}
	for n, c := range files {
		if err := os.WriteFile(filepath.Join(dir, n), []byte(c), 0o600); err != nil {
			return err
		}
	}
	fmt.Printf("wrote %s and %s\n", filepath.Join(dir, "vyos-bootstrap.sh"), filepath.Join(dir, "user-data.yaml"))
	if res.WGPublic != "" {
		fmt.Println("router WireGuard public key:", res.WGPublic)
	}
	return nil
}
