package libvirt

import (
	"bytes"
	"context"
	"path/filepath"
	"strings"
	"testing"
	"text/template"

	"slrz.net/runtopo/topology"

	libvirtxml "libvirt.org/libvirt-go-xml"
)

func TestValidDomainXML(t *testing.T) {
	topoFiles, err := filepath.Glob("testdata/*.dot")
	if err != nil {
		t.Fatal(err)
	}

	for _, file := range topoFiles {
		topo, err := topology.ParseFile(file, topology.WithAutoMgmtNetwork)
		if err != nil {
			t.Fatal(err)
		}

		r := NewRunner()
		if err := r.buildInventory(topo); err != nil {
			t.Fatal(err)
		}

		tmpl, err := template.New("").
			Funcs(templateFuncs).
			Parse(domainTemplateText)
		if err != nil {
			t.Fatal(err)
		}

		var buf bytes.Buffer
		for _, d := range r.devices {
			if err := tmpl.Execute(&buf, d.templateArgs()); err != nil {
				t.Errorf("domain %s: %v", d.name, err)
			}
			domXML := buf.Bytes()
			if err := validateDomainXML(domXML); err != nil {
				t.Errorf("domain %s: %v", d.name, err)
			}
			buf.Reset()
		}
	}
}

func TestDnsmasqHostsFile(t *testing.T) {
	topo, err := topology.ParseFile("testdata/leafspine.dot", topology.WithAutoMgmtNetwork)
	if err != nil {
		t.Fatal(err)
	}

	r := NewRunner()
	if err := r.buildInventory(topo); err != nil {
		t.Fatal(err)
	}
	ctx := context.Background()
	content := generateDnsmasqHostsFile(gatherHosts(ctx, r, topo))
	if err != nil {
		t.Fatal(err)
	}
	lines := strings.Split(string(content), "\n")
	if lines[len(lines)-1] == "" {
		lines = lines[:len(lines)-1]
	}

	ndevices := 0
	for _, d := range topo.Devices() {
		if d.Attr("no_mgmt") != "" {
			continue
		}
		if topology.HasFunction(&d, topology.OOBServer,
			topology.OOBSwitch, topology.Fake) {
			continue
		}
		ndevices++
	}

	if n := len(lines); n != ndevices {
		t.Errorf("got %d entries, want %d", n, ndevices)
	}

	for i, l := range lines {
		xs := strings.Split(l, ",")
		if len(xs) != 3 {
			t.Errorf("line %d invalid: %q\n", i+1, l)
		}
	}
}

func TestDomainPXEBoot(t *testing.T) {
	topo, err := topology.ParseFile("testdata/pxehost.dot")
	if err != nil {
		t.Fatal(err)
	}

	r := NewRunner()
	if err := r.buildInventory(topo); err != nil {
		t.Fatal(err)
	}

	tmpl, err := template.New("").
		Funcs(templateFuncs).
		Parse(domainTemplateText)
	if err != nil {
		t.Fatal(err)
	}

	var buf bytes.Buffer
	d := r.devices["host1"]
	if err := tmpl.Execute(&buf, d.templateArgs()); err != nil {
		t.Errorf("domain %s: %v", d.name, err)
	}
	dom := new(libvirtxml.Domain)
	if err := dom.Unmarshal(buf.String()); err != nil {
		t.Fatalf("domain %s: %v", d.name, err)
	}
	disks := dom.Devices.Disks
	if len(disks) == 0 {
		t.Fatalf("domain %s: no disks", d.name)
	}
	if d0 := disks[0]; d0.Boot == nil || d0.Boot.Order != 2 {
		t.Fatalf("domain %s: unexpected boot order: %#v", d.name, d0.Boot)
	}
	pxeOK := false
	for _, intf := range dom.Devices.Interfaces {
		if intf.Boot != nil && intf.Boot.Order == 1 {
			pxeOK = true
		}
	}
	if !pxeOK {
		t.Fatalf("domain %s: no interface configured for booting", d.name)
	}
}