//go:build integration

package steme

import (
	"context"
	"testing"
	"time"
)

// Integration tests for Assert operations including object types and validation.

// TestIntegration_Assert_Query_Roundtrip tests creating an assertion and querying it back.
func TestIntegration_Assert_Query_Roundtrip(t *testing.T) {
	client := getTestClient(t)
	ctx := context.Background()

	// Create a unique subject to avoid interference
	subject := uniqueSubject("IntegrationTest_Entity")
	predicate := "has_value"
	expectedValue := "test_value_roundtrip"

	// Create assertion
	assertion := NewAssertion(subject, predicate).
		WithText(expectedValue).
		WithConfidence(0.95).
		WithLifecycle(LifecycleApproved).
		WithSourceClass(SourceClassClinical).
		WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
		Build()

	hash, err := client.Assert(ctx, assertion)
	if err != nil {
		t.Fatalf("Assert() failed: %v", err)
	}

	if len(hash) != 64 {
		t.Errorf("Assert() hash length = %d, want 64", len(hash))
	}

	t.Logf("Created assertion with hash: %s", hash)

	// Small delay to ensure indexing completes
	time.Sleep(100 * time.Millisecond)

	// Query it back
	params := NewQuery().
		WithSubject(subject).
		WithPredicate(predicate).
		Build()

	result, err := client.Query(ctx, params)
	if err != nil {
		t.Fatalf("Query() failed: %v", err)
	}

	if result.TotalCount == 0 {
		t.Fatal("Query() returned no results")
	}

	// Verify we got our assertion back
	found := false
	for _, a := range result.Assertions {
		if a.Hash == hash {
			found = true

			if a.Subject != subject {
				t.Errorf("Subject = %s, want %s", a.Subject, subject)
			}

			if a.Predicate != predicate {
				t.Errorf("Predicate = %s, want %s", a.Predicate, predicate)
			}

			if a.Object.Type != "Text" {
				t.Errorf("Object.Type = %s, want Text", a.Object.Type)
			}

			if a.Object.Value != expectedValue {
				t.Errorf("Object.Value = %v, want %s", a.Object.Value, expectedValue)
			}

			if a.Confidence != 0.95 {
				t.Errorf("Confidence = %f, want 0.95", a.Confidence)
			}

			if a.Lifecycle != LifecycleApproved {
				t.Errorf("Lifecycle = %s, want Approved", a.Lifecycle)
			}

			if a.SourceClass != SourceClassClinical {
				t.Errorf("SourceClass = %s, want Clinical", a.SourceClass)
			}

			if len(a.Signatures) == 0 {
				t.Error("Assertion has no signatures")
			}

			break
		}
	}

	if !found {
		t.Errorf("Did not find assertion with hash %s in query results", hash)
	}
}

// TestIntegration_Assert_AllObjectTypes tests all object value types.
func TestIntegration_Assert_AllObjectTypes(t *testing.T) {
	client := getTestClient(t)
	ctx := context.Background()

	subject := uniqueSubject("IntegrationTest_Types")

	tests := []struct {
		name      string
		predicate string
		builder   func(*AssertionBuilder) *AssertionBuilder
		checkType string
	}{
		{
			name:      "Text",
			predicate: "has_text",
			builder: func(b *AssertionBuilder) *AssertionBuilder {
				return b.WithText("hello world")
			},
			checkType: "Text",
		},
		{
			name:      "Number",
			predicate: "has_number",
			builder: func(b *AssertionBuilder) *AssertionBuilder {
				return b.WithNumber(42.5)
			},
			checkType: "Number",
		},
		{
			name:      "Boolean",
			predicate: "has_boolean",
			builder: func(b *AssertionBuilder) *AssertionBuilder {
				return b.WithBoolean(true)
			},
			checkType: "Boolean",
		},
		{
			name:      "Reference",
			predicate: "has_reference",
			builder: func(b *AssertionBuilder) *AssertionBuilder {
				return b.WithReference("Entity_123")
			},
			checkType: "Reference",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			builder := NewAssertion(subject, tt.predicate).
				WithConfidence(0.9).
				WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000")

			builder = tt.builder(builder)
			assertion := builder.Build()

			hash, err := client.Assert(ctx, assertion)
			if err != nil {
				t.Fatalf("Assert() for %s failed: %v", tt.name, err)
			}

			t.Logf("Created %s assertion with hash: %s", tt.name, hash)

			// Small delay for indexing
			time.Sleep(100 * time.Millisecond)

			// Query it back
			params := NewQuery().
				WithSubject(subject).
				WithPredicate(tt.predicate).
				Build()

			result, err := client.Query(ctx, params)
			if err != nil {
				t.Fatalf("Query() for %s failed: %v", tt.name, err)
			}

			if result.TotalCount == 0 {
				t.Fatalf("Query() for %s returned no results", tt.name)
			}

			// Verify object type
			if result.Assertions[0].Object.Type != tt.checkType {
				t.Errorf("Object.Type = %s, want %s", result.Assertions[0].Object.Type, tt.checkType)
			}
		})
	}
}

// TestIntegration_Assert_InvalidAssertion tests that invalid assertions are rejected.
func TestIntegration_Assert_InvalidAssertion(t *testing.T) {
	client := getTestClient(t)
	ctx := context.Background()

	tests := []struct {
		name      string
		assertion Assertion
		wantErr   bool
	}{
		{
			name: "missing source_hash",
			assertion: NewAssertion("Subject", "Predicate").
				WithText("value").
				Build(),
			wantErr: true,
		},
		{
			name: "invalid confidence",
			assertion: NewAssertion("Subject", "Predicate").
				WithText("value").
				WithConfidence(1.5).
				WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
				Build(),
			wantErr: true,
		},
		{
			name: "negative confidence",
			assertion: NewAssertion("Subject", "Predicate").
				WithText("value").
				WithConfidence(-0.1).
				WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
				Build(),
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := client.Assert(ctx, tt.assertion)
			if (err != nil) != tt.wantErr {
				t.Errorf("Assert() error = %v, wantErr %v", err, tt.wantErr)
			}

			if tt.wantErr && err != nil {
				t.Logf("Got expected error: %v", err)
			}
		})
	}
}

// TestIntegration_Lifecycle tests lifecycle stage filtering.
func TestIntegration_Lifecycle(t *testing.T) {
	client := getTestClient(t)
	ctx := context.Background()

	subject := uniqueSubject("IntegrationTest_Lifecycle")
	predicate := "has_status"

	// Create assertions with different lifecycle stages
	stages := []LifecycleStage{
		LifecycleProposed,
		LifecycleApproved,
		LifecycleDeprecated,
	}

	for _, stage := range stages {
		assertion := NewAssertion(subject, predicate).
			WithText(string(stage)).
			WithLifecycle(stage).
			WithConfidence(0.9).
			WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
			Build()

		hash, err := client.Assert(ctx, assertion)
		if err != nil {
			t.Fatalf("Assert() for lifecycle %s failed: %v", stage, err)
		}

		t.Logf("Created assertion with lifecycle %s: %s", stage, hash)
	}

	// Small delay for indexing
	time.Sleep(100 * time.Millisecond)

	// Query for only Approved lifecycle
	params := NewQuery().
		WithSubject(subject).
		WithPredicate(predicate).
		WithLifecycle(LifecycleApproved).
		Build()

	result, err := client.Query(ctx, params)
	if err != nil {
		t.Fatalf("Query() failed: %v", err)
	}

	// Should only get the Approved assertion
	for _, a := range result.Assertions {
		if a.Lifecycle != LifecycleApproved {
			t.Errorf("Got assertion with lifecycle %s, want Approved only", a.Lifecycle)
		}
	}

	t.Logf("Lifecycle filtering returned %d Approved assertions", result.TotalCount)
}
