package domain

import (
	"errors"
	"testing"
)

func TestIsNil(t *testing.T) {
	cases := []struct {
		name string
		u    UUID
		want bool
	}{
		{"empty string", UUID(""), true},
		{"single char", UUID("x"), false},
		{"uuid string", UUID("550e8400-e29b-41d4-a716-446655440000"), false},
		{"nil literal", UUID(""), true},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			got := c.u.IsNil()
			if got != c.want {
				t.Errorf("UUID(%q).IsNil() = %v, want %v", c.u, got, c.want)
			}
		})
	}
}

func TestCanTransition(t *testing.T) {
	type tc struct {
		name string
		from string
		to   string
		want bool
	}
	var cases []tc

	for from, targets := range ValidSignalTransitions {
		for _, to := range targets {
			cases = append(cases, tc{from + "->" + to, from, to, true})
		}
	}

	disallowed := []tc{
		{"raised->raised", SignalRaised, SignalRaised, false},
		{"resolved->raised", SignalResolved, SignalRaised, false},
		{"failed->raised", SignalFailed, SignalRaised, false},
		{"acknowledged->raised", SignalAcknowledged, SignalRaised, false},
		{"muted->resolved", SignalMuted, SignalResolved, false},
		{"acting->acknowledged", SignalActing, SignalAcknowledged, false},
	}
	cases = append(cases, disallowed...)

	cases = append(cases,
		tc{"unknown source", "nonexistent", SignalRaised, false},
		tc{"unknown target", SignalRaised, "nonexistent", false},
	)

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			s := &Signal{State: c.from}
			got := s.CanTransition(c.to)
			if got != c.want {
				t.Errorf("CanTransition(%q -> %q) = %v, want %v", c.from, c.to, got, c.want)
			}
		})
	}
}

func TestSentinelErrors(t *testing.T) {
	cases := []struct {
		name string
		err  error
		msg  string
	}{
		{"ErrNotFound", ErrNotFound, "entity not found"},
		{"ErrInvalidTransition", ErrInvalidTransition, "invalid lifecycle transition"},
		{"ErrApprovalRequired", ErrApprovalRequired, "operator approval required"},
		{"ErrAutonomyBlocked", ErrAutonomyBlocked, "autonomy policy blocks this action"},
		{"ErrConflict", ErrConflict, "concurrent modification conflict"},
		{"ErrCircuitOpen", ErrCircuitOpen, "circuit breaker open for target"},
		{"ErrAbstractType", ErrAbstractType, "cannot instantiate abstract entity type"},
		{"ErrInvalidEdge", ErrInvalidEdge, "relationship endpoint type mismatch"},
		{"ErrCardinality", ErrCardinality, "relationship cardinality violation"},
		{"ErrSeedHashMismatch", ErrSeedHashMismatch, "seed content hash mismatch"},
		{"ErrAlreadyExists", ErrAlreadyExists, "entity already exists"},
		{"ErrQuarantined", ErrQuarantined, "pattern is quarantined"},
		{"ErrSkillDeprecated", ErrSkillDeprecated, "skill is deprecated"},
		{"ErrInvalidInput", ErrInvalidInput, "invalid input"},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			if c.err == nil {
				t.Fatal("sentinel error is nil")
			}
			if !errors.Is(c.err, c.err) {
				t.Errorf("errors.Is failed for %s", c.name)
			}
			if c.err.Error() != c.msg {
				t.Errorf("Error() = %q, want %q", c.err.Error(), c.msg)
			}
		})
	}
}

func TestSignalTransitionsComplete(t *testing.T) {
	// Non-terminal states must be keys in ValidSignalTransitions.
	// SignalResolved is a terminal state (no outgoing transitions) and is
	// intentionally absent from the map.
	nonTerminal := []string{
		SignalRaised,
		SignalAcknowledged,
		SignalActing,
		SignalMuted,
		SignalFailed,
	}
	for _, state := range nonTerminal {
		targets, ok := ValidSignalTransitions[state]
		if !ok {
			t.Errorf("non-terminal state %q missing from ValidSignalTransitions", state)
			continue
		}
		if len(targets) == 0 {
			t.Errorf("state %q maps to empty transition list", state)
		}
	}

	// Resolved is terminal: it should not appear as a source key.
	if _, ok := ValidSignalTransitions[SignalResolved]; ok {
		t.Errorf("terminal state %q should not have outgoing transitions", SignalResolved)
	}

	// No state anywhere in the map may map to nil/empty.
	for state, targets := range ValidSignalTransitions {
		if len(targets) == 0 {
			t.Errorf("state %q maps to empty/nil transition list", state)
		}
	}
}
