package steme

import (
	"testing"
)

// TestCanonicalMessage tests the canonical message generation for signing.
func TestCanonicalMessage(t *testing.T) {
	assertion := NewAssertion("Tesla_Inc", "has_revenue").
		WithNumber(96.7).
		WithConfidence(0.95).
		WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
		Build()

	msg1, err := canonicalAssertionMessage(&assertion)
	if err != nil {
		t.Fatalf("canonicalAssertionMessage() failed: %v", err)
	}

	// Verify format matches server expectation: "{subject}:{predicate}"
	expected := "Tesla_Inc:has_revenue"
	if string(msg1) != expected {
		t.Errorf("canonicalAssertionMessage() = %q, want %q", string(msg1), expected)
	}

	// Same assertion should produce same message
	msg2, err := canonicalAssertionMessage(&assertion)
	if err != nil {
		t.Fatalf("canonicalAssertionMessage() failed: %v", err)
	}

	if string(msg1) != string(msg2) {
		t.Errorf("Canonical message not deterministic")
	}

	// Different subject/predicate should produce different message
	assertion2 := NewAssertion("Tesla_Inc", "has_employees"). // Different predicate
									WithNumber(97.0).
									WithConfidence(0.95).
									WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
									Build()

	msg3, err := canonicalAssertionMessage(&assertion2)
	if err != nil {
		t.Fatalf("canonicalAssertionMessage() failed: %v", err)
	}

	if string(msg1) == string(msg3) {
		t.Errorf("Different assertions produced same canonical message")
	}
}

// TestCanonicalMessageAllObjectTypes tests canonical message with all object types.
// The canonical message is just "{subject}:{predicate}" - object type doesn't affect it.
func TestCanonicalMessageAllObjectTypes(t *testing.T) {
	types := []struct {
		name string
		obj  ObjectValue
	}{
		{"text", NewTextValue("hello")},
		{"number", NewNumberValue(42.5)},
		{"boolean", NewBooleanValue(true)},
		{"reference", NewReferenceValue("Entity_123")},
	}

	for _, tt := range types {
		t.Run(tt.name, func(t *testing.T) {
			a := NewAssertion("Subject", "Predicate").
				WithConfidence(0.5).
				WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
				Build()
			a.Object = tt.obj

			msg, err := canonicalAssertionMessage(&a)
			if err != nil {
				t.Errorf("canonicalAssertionMessage() failed for %s: %v", tt.name, err)
			}
			// Canonical message is "{subject}:{predicate}" regardless of object type
			expected := "Subject:Predicate"
			if string(msg) != expected {
				t.Errorf("Expected %q, got %q", expected, string(msg))
			}
		})
	}
}

// TestCanonicalMessageEdgeCases tests edge cases in canonical message generation.
func TestCanonicalMessageEdgeCases(t *testing.T) {
	tests := []struct {
		name     string
		build    func() Assertion
		wantErr  bool
		expected string
	}{
		{
			name: "zero confidence",
			build: func() Assertion {
				return NewAssertion("S", "P").
					WithText("v").
					WithConfidence(0.0).
					WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
					Build()
			},
			wantErr:  false,
			expected: "S:P",
		},
		{
			name: "max confidence",
			build: func() Assertion {
				return NewAssertion("S", "P").
					WithText("v").
					WithConfidence(1.0).
					WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
					Build()
			},
			wantErr:  false,
			expected: "S:P",
		},
		{
			name: "empty strings",
			build: func() Assertion {
				return NewAssertion("", "").
					WithText("v").
					WithConfidence(0.5).
					WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
					Build()
			},
			wantErr:  false,
			expected: ":",
		},
		{
			name: "special characters",
			build: func() Assertion {
				return NewAssertion("Subject:With:Colons", "Predicate").
					WithText("v").
					WithConfidence(0.5).
					WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
					Build()
			},
			wantErr:  false,
			expected: "Subject:With:Colons:Predicate",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			a := tt.build()
			msg, err := canonicalAssertionMessage(&a)
			if (err != nil) != tt.wantErr {
				t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
			}
			if !tt.wantErr && string(msg) != tt.expected {
				t.Errorf("got %q, want %q", string(msg), tt.expected)
			}
		})
	}
}
