package engine

import (
	"context"
	"testing"
	"time"

	"git.cloudinit.dev/coreci/orca/internal/store"
)

func TestPeerRegistry_AddAndGet(t *testing.T) {
	r := NewPeerRegistry()
	p := &Peer{
		NodeID:     "node-1",
		Address:    "localhost:8443",
		ServerName: "node-1.orca",
		CAPath:     "/etc/orca/ca.pem",
	}
	if err := r.Add(p); err != nil {
		t.Fatalf("Add: %v", err)
	}
	got := r.Get("node-1")
	if got == nil {
		t.Fatal("Get: returned nil after Add")
	}
	if got.NodeID != "node-1" || got.Address != "localhost:8443" ||
		got.ServerName != "node-1.orca" || got.CAPath != "/etc/orca/ca.pem" {
		t.Errorf("Get: fields mismatch: %+v", got)
	}
}

func TestPeerRegistry_AddNil(t *testing.T) {
	r := NewPeerRegistry()
	if err := r.Add(nil); err == nil {
		t.Fatal("Add(nil): expected error, got nil")
	}
}

func TestPeerRegistry_AddMissingID(t *testing.T) {
	r := NewPeerRegistry()
	if err := r.Add(&Peer{Address: "a"}); err == nil {
		t.Fatal("Add(empty NodeID): expected error, got nil")
	}
}

func TestPeerRegistry_Remove(t *testing.T) {
	r := NewPeerRegistry()
	p := &Peer{NodeID: "node-r", Address: "a"}
	if err := r.Add(p); err != nil {
		t.Fatalf("Add: %v", err)
	}
	if !r.Remove("node-r") {
		t.Fatal("Remove: returned false for existing peer")
	}
	if got := r.Get("node-r"); got != nil {
		t.Errorf("Get after Remove: want nil, got %+v", got)
	}
	if r.Remove("node-r") {
		t.Error("Remove second time: want false, got true")
	}
}

func TestPeerRegistry_All(t *testing.T) {
	r := NewPeerRegistry()
	for _, id := range []string{"node-c", "node-a", "node-b"} {
		if err := r.Add(&Peer{NodeID: id, Address: "a"}); err != nil {
			t.Fatalf("Add %s: %v", id, err)
		}
	}
	got, err := r.All(context.Background())
	if err != nil {
		t.Fatalf("All: %v", err)
	}
	if len(got) != 3 {
		t.Fatalf("All: got %d, want 3", len(got))
	}
	want := []string{"node-a", "node-b", "node-c"}
	for i, w := range want {
		if got[i].NodeID != w {
			t.Errorf("All[%d]: got %s, want %s (not sorted by NodeID)", i, got[i].NodeID, w)
		}
	}
}

func TestPeerRegistry_All_Empty(t *testing.T) {
	r := NewPeerRegistry()
	got, err := r.All(context.Background())
	if err != nil {
		t.Fatalf("All on empty: %v", err)
	}
	if len(got) != 0 {
		t.Errorf("All on empty: got %d, want 0", len(got))
	}
}

func TestPeerRegistry_Len(t *testing.T) {
	r := NewPeerRegistry()
	if r.Len() != 0 {
		t.Errorf("Len on empty: got %d, want 0", r.Len())
	}
	if err := r.Add(&Peer{NodeID: "n1", Address: "a"}); err != nil {
		t.Fatalf("Add n1: %v", err)
	}
	if err := r.Add(&Peer{NodeID: "n2", Address: "a"}); err != nil {
		t.Fatalf("Add n2: %v", err)
	}
	if r.Len() != 2 {
		t.Errorf("Len: got %d, want 2", r.Len())
	}
}

func TestPeerRegistry_UpdateLastSeen(t *testing.T) {
	r := NewPeerRegistry()
	old := time.Now().Add(-1 * time.Hour).UTC()
	p := &Peer{
		NodeID:   "node-u",
		Address:  "a",
		LastSeen: old,
		Capacity: &store.NodeCapacity{NodeID: "node-u", CPUMillicores: 1000, MemoryMiB: 1024, DiskMiB: 1024},
	}
	if err := r.Add(p); err != nil {
		t.Fatalf("Add: %v", err)
	}
	r.UpdateLastSeen("node-u")
	got := r.Get("node-u")
	if got == nil {
		t.Fatal("Get: nil after UpdateLastSeen")
	}
	if !got.LastSeen.After(old) {
		t.Errorf("UpdateLastSeen: LastSeen not bumped; old=%v now=%v", old, got.LastSeen)
	}
	if time.Since(got.LastSeen) > 5*time.Second {
		t.Errorf("UpdateLastSeen: LastSeen not recent: %v", got.LastSeen)
	}
	r.UpdateLastSeen("nonexistent")
}
