package cluster

import (
	"errors"
	"testing"
)

func TestIsLeadEligible(t *testing.T) {
	cases := []struct {
		name string
		kind NodeKind
		want bool
	}{
		{"linux", NodeKindLinux, true},
		{"proxmox", NodeKindProxmox, false},
		{"empty", "", false},
		{"unknown", NodeKind("foo"), false},
		{"localhost", NodeKind("localhost"), false},
	}
	for _, tc := range cases {
		if got := IsLeadEligible(tc.kind); got != tc.want {
			t.Errorf("IsLeadEligible(%q) = %v, want %v", tc.kind, got, tc.want)
		}
	}
}

func TestValidateLeadRotation_LinuxOK(t *testing.T) {
	nodes := []NodeInfo{
		{Hostname: "n1", Kind: NodeKindLinux},
		{Hostname: "n2", Kind: NodeKindLinux},
		{Hostname: "pve1", Kind: NodeKindProxmox},
	}
	if err := ValidateLeadRotation("n2", nodes); err != nil {
		t.Errorf("ValidateLeadRotation(n2): err = %v, want nil", err)
	}
	if err := ValidateLeadRotation("n1", nodes); err != nil {
		t.Errorf("ValidateLeadRotation(n1): err = %v, want nil", err)
	}
}

func TestValidateLeadRotation_ProxmoxRefused(t *testing.T) {
	nodes := []NodeInfo{
		{Hostname: "n1", Kind: NodeKindLinux},
		{Hostname: "pve1", Kind: NodeKindProxmox},
	}
	err := ValidateLeadRotation("pve1", nodes)
	if err == nil {
		t.Fatal("ValidateLeadRotation(pve1): expected error, got nil")
	}
	if !errors.Is(err, ErrProxmoxNotLead) {
		t.Errorf("err = %v, want ErrProxmoxNotLead", err)
	}
	if got := err.Error(); got != "Proxmox nodes cannot hold the cluster lead role (R-003)" {
		t.Errorf("err message = %q, want R-003 text verbatim", got)
	}
}

func TestValidateLeadRotation_UnknownNode(t *testing.T) {
	nodes := []NodeInfo{
		{Hostname: "n1", Kind: NodeKindLinux},
	}
	err := ValidateLeadRotation("ghost", nodes)
	if err == nil {
		t.Fatal("ValidateLeadRotation(ghost): expected error, got nil")
	}
	if !errors.Is(err, ErrNodeNotRegistered) {
		t.Errorf("err = %v, want ErrNodeNotRegistered", err)
	}
}

func TestValidateLeadRotation_EmptyList(t *testing.T) {
	err := ValidateLeadRotation("anyone", nil)
	if err == nil {
		t.Fatal("ValidateLeadRotation on empty list: expected error, got nil")
	}
	if !errors.Is(err, ErrNodeNotRegistered) {
		t.Errorf("err = %v, want ErrNodeNotRegistered", err)
	}
}

func TestValidateLeadRotation_IneligibleKindRegistered(t *testing.T) {
	// A node registered with a kind that is neither linux nor
	// proxmox (e.g. the auto-registered "localhost" kind) is
	// rejected as ineligible, not as unregistered.
	nodes := []NodeInfo{
		{Hostname: "self", Kind: NodeKind("localhost")},
	}
	err := ValidateLeadRotation("self", nodes)
	if err == nil {
		t.Fatal("expected error for localhost kind, got nil")
	}
	if !errors.Is(err, ErrProxmoxNotLead) {
		t.Errorf("err = %v, want wrapped ErrProxmoxNotLead (ineligible)", err)
	}
}

func TestValidateLeadRotation_CaseSensitive(t *testing.T) {
	// Hostnames are case-normalized at the store layer; the rule
	// matches exactly. "N1" is NOT the same as "n1".
	nodes := []NodeInfo{
		{Hostname: "n1", Kind: NodeKindLinux},
	}
	if err := ValidateLeadRotation("N1", nodes); !errors.Is(err, ErrNodeNotRegistered) {
		t.Errorf("N1 (case mismatch): err = %v, want ErrNodeNotRegistered", err)
	}
}

func TestNodeKindString(t *testing.T) {
	if got := NodeKindLinux.String(); got != "linux" {
		t.Errorf("Linux.String() = %q", got)
	}
	if got := NodeKindProxmox.String(); got != "proxmox" {
		t.Errorf("Proxmox.String() = %q", got)
	}
	// Uppercase constructor should lower-case.
	if got := NodeKind("PROXMOX").String(); got != "proxmox" {
		t.Errorf("PROXMOX.String() = %q, want proxmox", got)
	}
}
