package main

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"
)

func TestVyosCommands(t *testing.T) {
	r, err := vyosCommands(vyosOpts{Hostname: "h", WANIf: "eth0", LANIf: "eth1", TenantCIDR: "10.77.0.0/24",
		DNS: "1.1.1.1", WireGuard: true, WGPort: 51820, WGCIDR: "10.99.0.1/24"})
	if err != nil {
		t.Fatal(err)
	}
	s := strings.Join(r.Commands, "\n")
	for _, want := range []string{
		"interfaces ethernet eth1 address 10.77.0.1/24",
		"range 0 start 10.77.0.100", "range 0 stop 10.77.0.250",
		"nat source rule 100 translation address masquerade",
		"interfaces wireguard wg0 port 51820",
	} {
		if !strings.Contains(s, want) {
			t.Errorf("missing %q", want)
		}
	}
	if len(r.WGPublic) != 44 {
		t.Errorf("wg public key length %d", len(r.WGPublic))
	}
}

func TestVyosRejectsBadCIDR(t *testing.T) {
	for _, c := range []string{"nope", "10.0.0.0/30", "10.0.0.0/8", "fd00::/64"} {
		if _, err := vyosCommands(vyosOpts{TenantCIDR: c}); err == nil {
			t.Errorf("%s should fail", c)
		}
	}
}

func mockPVE(t *testing.T, posts *[]url.Values) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Authorization") != "PVEAPIToken=u@pve!a=s" {
			http.Error(w, "no", 401)
			return
		}
		data := any(nil)
		switch {
		case r.Method == "POST":
			r.ParseForm()
			*posts = append(*posts, r.PostForm)
		case r.URL.Path == "/api2/json/version":
			data = map[string]string{"version": "8.2.4"}
		case strings.HasSuffix(r.URL.Path, "/network"):
			data = []map[string]any{{"iface": "vmbr0", "type": "bridge"}, {"iface": "vmbr1", "type": "bridge", "bridge_vlan_aware": 1}}
		case r.URL.Path == "/api2/json/cluster/resources":
			data = []map[string]any{{"vmid": 9100, "name": "vyos-router", "status": "running"}}
		}
		json.NewEncoder(w).Encode(map[string]any{"data": data})
	}))
}

func TestCertifyAllPass(t *testing.T) {
	var posts []url.Values
	s := mockPVE(t, &posts)
	defer s.Close()
	c := newPVE(s.URL, "u@pve!a=s", "pve", false, false, func(string) {})
	rep, err := buildReport(c, certOpts{"vmbr0", "vmbr1"})
	if err != nil {
		t.Fatal(err)
	}
	if !rep.AllPass || rep.RequestID == "" {
		t.Fatalf("%+v", rep)
	}
}

func TestCertifyFailsWithoutVyos(t *testing.T) {
	var posts []url.Values
	s := mockPVE(t, &posts)
	defer s.Close()
	c := newPVE(s.URL, "u@pve!a=s", "pve", false, false, func(string) {})
	rep, _ := buildReport(c, certOpts{"vmbr0", "vmbr9"})
	if rep.AllPass {
		t.Fatal("missing bridge must fail")
	}
}

func TestDeployCreatesAndStarts(t *testing.T) {
	var posts []url.Values
	s := mockPVE(t, &posts)
	defer s.Close()
	c := newPVE(s.URL, "u@pve!a=s", "pve", false, false, func(string) {})
	err := c.createVyOS(deployOpts{VMID: 9100, Name: "vyos-router", Cores: 2, MemoryMB: 2048, DiskGB: 10,
		Storage: "local-lvm", ISO: "local:iso/v.iso", WANBridge: "vmbr0", LANBridge: "vmbr1"})
	if err != nil || len(posts) != 2 {
		t.Fatalf("err=%v posts=%d", err, len(posts))
	}
	if posts[0].Get("net0") != "virtio,bridge=vmbr0" || posts[0].Get("net1") != "virtio,bridge=vmbr1" {
		t.Errorf("nics: %v", posts[0])
	}
}

func TestDryRunNoNetwork(t *testing.T) {
	var lines []string
	c := newPVE("http://127.0.0.1:1", "", "pve", false, true, func(s string) { lines = append(lines, s) })
	if err := c.createVyOS(deployOpts{VMID: 1, Name: "x", ISO: "i", Storage: "s", WANBridge: "a", LANBridge: "b"}); err != nil {
		t.Fatal(err)
	}
	if len(lines) != 2 {
		t.Errorf("lines=%v", lines)
	}
}
