package plan

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"metis/pkg/inventory"
)

func TestFilesAndInjectWithSecretsAndOverlays(t *testing.T) {
	vault := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/v1/kv/data/atlas/nodes/titan-15" {
			http.NotFound(w, r)
			return
		}
		_ = json.NewEncoder(w).Encode(map[string]any{
			"data": map[string]any{
				"data": map[string]any{
					"cloud_init": "#cloud-config\nmanage_etc_hosts: true\n",
					"k3s_token":  "secret-token",
					"extra":      map[string]string{"foo": "bar"},
				},
			},
		})
	}))
	defer vault.Close()
	t.Setenv("VAULT_ADDR", vault.URL)
	t.Setenv("VAULT_TOKEN", "tok")

	dir := t.TempDir()
	bootOverlay := filepath.Join(dir, "boot-overlay")
	rootOverlay := filepath.Join(dir, "root-overlay")
	if err := os.MkdirAll(filepath.Join(bootOverlay, "over"), 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.MkdirAll(filepath.Join(rootOverlay, "etc"), 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(bootOverlay, "over", "cmdline.txt"), []byte("console=tty1"), 0o644); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(rootOverlay, "etc", "issue"), []byte("hello"), 0o644); err != nil {
		t.Fatal(err)
	}
	inv := &inventory.Inventory{
		Classes: []inventory.NodeClass{{
			Name:        "c1",
			Arch:        "arm64",
			OS:          "linux",
			Image:       "file:///tmp/base.img",
			BootOverlay: bootOverlay,
			RootOverlay: rootOverlay,
		}},
		Nodes: []inventory.NodeSpec{{
			Name:          "titan-15",
			Class:         "c1",
			Hostname:      "titan-15",
			IP:            "192.168.22.43",
			K3sRole:       "agent",
			SSHUser:       "atlas",
			SSHAuthorized: []string{"ssh-ed25519 AAA"},
		}},
	}

	files, err := Files(inv, "titan-15")
	if err != nil {
		t.Fatalf("Files: %v", err)
	}
	var sawSecret, sawBootOverlay, sawRootOverlay, sawCloudInit bool
	for _, f := range files {
		switch {
		case f.Path == "etc/metis/secrets.json":
			sawSecret = true
		case f.Path == "over/cmdline.txt":
			sawBootOverlay = true
		case f.Path == "etc/issue":
			sawRootOverlay = true
		case f.Path == "user-data":
			sawCloudInit = strings.Contains(string(f.Content), "manage_etc_hosts: true")
		}
	}
	if !sawSecret || !sawBootOverlay || !sawRootOverlay || !sawCloudInit {
		t.Fatalf("missing generated files: secret=%v boot=%v root=%v cloudinit=%v", sawSecret, sawBootOverlay, sawRootOverlay, sawCloudInit)
	}

	bootDir := filepath.Join(dir, "boot")
	rootDir := filepath.Join(dir, "root")
	if err := os.MkdirAll(bootDir, 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.MkdirAll(rootDir, 0o755); err != nil {
		t.Fatal(err)
	}
	if err := Inject(inv, "titan-15", bootDir, rootDir); err != nil {
		t.Fatalf("Inject: %v", err)
	}
	if _, err := os.Stat(filepath.Join(bootDir, "over", "cmdline.txt")); err != nil {
		t.Fatalf("expected boot overlay file: %v", err)
	}
	if _, err := os.Stat(filepath.Join(rootDir, "etc/metis/node.json")); err != nil {
		t.Fatalf("expected injected rootfs file: %v", err)
	}
}

func TestNextRunStale(t *testing.T) {
	if !NextRunStale(time.Now().Add(-2*time.Hour), time.Hour) {
		t.Fatal("expected stale run")
	}
	if NextRunStale(time.Now(), time.Hour) {
		t.Fatal("did not expect fresh run to be stale")
	}
}

func TestAllowK3sNodeLabelRules(t *testing.T) {
	if allowK3sNodeLabel("agent", "node-role.kubernetes.io/worker") {
		t.Fatal("agent should block node-role labels")
	}
	if !allowK3sNodeLabel("server", "node-role.kubernetes.io/worker") {
		t.Fatal("server should allow node-role labels")
	}
}
