package sandbox

import (
	"os"
	"path/filepath"
	"testing"
	"time"
)

func TestSaveAndLoadState(t *testing.T) {
	// Use temp dir for test
	tmpDir := t.TempDir()
	origHome := os.Getenv("HOME")
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", origHome)

	state := &SandboxState{
		Name:      "test-sandbox",
		CreatedAt: time.Date(2026, 2, 25, 10, 0, 0, 0, time.UTC),
		Domain:    "test.example.com",
		Status:    StatusRunning,
		Servers: []ServerState{
			{ID: 1, Name: "sbx-test-1", IP: "1.1.1.1", Role: "nameserver", FloatingIP: "10.0.0.1", WgIP: "10.0.0.1"},
			{ID: 2, Name: "sbx-test-2", IP: "2.2.2.2", Role: "nameserver", FloatingIP: "10.0.0.2", WgIP: "10.0.0.2"},
			{ID: 3, Name: "sbx-test-3", IP: "3.3.3.3", Role: "node", WgIP: "10.0.0.3"},
			{ID: 4, Name: "sbx-test-4", IP: "4.4.4.4", Role: "node", WgIP: "10.0.0.4"},
			{ID: 5, Name: "sbx-test-5", IP: "5.5.5.5", Role: "node", WgIP: "10.0.0.5"},
		},
	}

	if err := SaveState(state); err != nil {
		t.Fatalf("SaveState() error = %v", err)
	}

	// Verify file exists
	expected := filepath.Join(tmpDir, ".orama", "sandboxes", "test-sandbox.yaml")
	if _, err := os.Stat(expected); err != nil {
		t.Fatalf("state file not created at %s: %v", expected, err)
	}

	// Load back
	loaded, err := LoadState("test-sandbox")
	if err != nil {
		t.Fatalf("LoadState() error = %v", err)
	}

	if loaded.Name != "test-sandbox" {
		t.Errorf("name = %s, want test-sandbox", loaded.Name)
	}
	if loaded.Domain != "test.example.com" {
		t.Errorf("domain = %s, want test.example.com", loaded.Domain)
	}
	if loaded.Status != StatusRunning {
		t.Errorf("status = %s, want running", loaded.Status)
	}
	if len(loaded.Servers) != 5 {
		t.Errorf("servers = %d, want 5", len(loaded.Servers))
	}
}

func TestLoadState_NotFound(t *testing.T) {
	tmpDir := t.TempDir()
	origHome := os.Getenv("HOME")
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", origHome)

	_, err := LoadState("nonexistent")
	if err == nil {
		t.Error("LoadState() expected error for nonexistent sandbox")
	}
}

func TestDeleteState(t *testing.T) {
	tmpDir := t.TempDir()
	origHome := os.Getenv("HOME")
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", origHome)

	state := &SandboxState{
		Name:   "to-delete",
		Status: StatusRunning,
	}
	if err := SaveState(state); err != nil {
		t.Fatalf("SaveState() error = %v", err)
	}

	if err := DeleteState("to-delete"); err != nil {
		t.Fatalf("DeleteState() error = %v", err)
	}

	_, err := LoadState("to-delete")
	if err == nil {
		t.Error("LoadState() should fail after DeleteState()")
	}
}

func TestListStates(t *testing.T) {
	tmpDir := t.TempDir()
	origHome := os.Getenv("HOME")
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", origHome)

	// Create 2 sandboxes
	for _, name := range []string{"sandbox-a", "sandbox-b"} {
		if err := SaveState(&SandboxState{Name: name, Status: StatusRunning}); err != nil {
			t.Fatalf("SaveState(%s) error = %v", name, err)
		}
	}

	states, err := ListStates()
	if err != nil {
		t.Fatalf("ListStates() error = %v", err)
	}
	if len(states) != 2 {
		t.Errorf("ListStates() returned %d, want 2", len(states))
	}
}

func TestFindActiveSandbox(t *testing.T) {
	tmpDir := t.TempDir()
	origHome := os.Getenv("HOME")
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", origHome)

	// No sandboxes
	active, err := FindActiveSandbox()
	if err != nil {
		t.Fatalf("FindActiveSandbox() error = %v", err)
	}
	if active != nil {
		t.Error("expected nil when no sandboxes exist")
	}

	// Add one running sandbox
	if err := SaveState(&SandboxState{Name: "active-one", Status: StatusRunning}); err != nil {
		t.Fatal(err)
	}
	if err := SaveState(&SandboxState{Name: "errored-one", Status: StatusError}); err != nil {
		t.Fatal(err)
	}

	active, err = FindActiveSandbox()
	if err != nil {
		t.Fatalf("FindActiveSandbox() error = %v", err)
	}
	if active == nil || active.Name != "active-one" {
		t.Errorf("FindActiveSandbox() = %v, want active-one", active)
	}
}

func TestToNodes(t *testing.T) {
	state := &SandboxState{
		Servers: []ServerState{
			{IP: "1.1.1.1", Role: "nameserver"},
			{IP: "2.2.2.2", Role: "node"},
		},
	}

	nodes := state.ToNodes("sandbox/root")
	if len(nodes) != 2 {
		t.Fatalf("ToNodes() returned %d nodes, want 2", len(nodes))
	}
	if nodes[0].Host != "1.1.1.1" {
		t.Errorf("node[0].Host = %s, want 1.1.1.1", nodes[0].Host)
	}
	if nodes[0].User != "root" {
		t.Errorf("node[0].User = %s, want root", nodes[0].User)
	}
	if nodes[0].VaultTarget != "sandbox/root" {
		t.Errorf("node[0].VaultTarget = %s, want sandbox/root", nodes[0].VaultTarget)
	}
	if nodes[0].SSHKey != "" {
		t.Errorf("node[0].SSHKey = %s, want empty (set by PrepareNodeKeys)", nodes[0].SSHKey)
	}
	if nodes[0].Environment != "sandbox" {
		t.Errorf("node[0].Environment = %s, want sandbox", nodes[0].Environment)
	}
}

func TestNameserverAndRegularNodes(t *testing.T) {
	state := &SandboxState{
		Servers: []ServerState{
			{Role: "nameserver"},
			{Role: "nameserver"},
			{Role: "node"},
			{Role: "node"},
			{Role: "node"},
		},
	}

	ns := state.NameserverNodes()
	if len(ns) != 2 {
		t.Errorf("NameserverNodes() = %d, want 2", len(ns))
	}

	regular := state.RegularNodes()
	if len(regular) != 3 {
		t.Errorf("RegularNodes() = %d, want 3", len(regular))
	}
}

func TestGenesisServer(t *testing.T) {
	state := &SandboxState{
		Servers: []ServerState{
			{Name: "first"},
			{Name: "second"},
		},
	}
	if state.GenesisServer().Name != "first" {
		t.Errorf("GenesisServer().Name = %s, want first", state.GenesisServer().Name)
	}

	empty := &SandboxState{}
	if empty.GenesisServer().Name != "" {
		t.Error("GenesisServer() on empty state should return zero value")
	}
}
