//go:build integration

package steme

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

// Integration tests for Trace/audit operations.

// TestIntegration_Trace tests the trace endpoint for audit trail querying.
func TestIntegration_Trace(t *testing.T) {
	client := getTestClient(t)
	ctx := context.Background()

	// Create a unique subject for this test
	subject := uniqueSubject("IntegrationTest_Trace")
	predicate := "has_trace_value"

	// Create an assertion to generate query activity
	assertion := NewAssertion(subject, predicate).
		WithText("test_trace_value").
		WithConfidence(0.95).
		WithLifecycle(LifecycleApproved).
		WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
		Build()

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

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

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

	// Perform a query to create audit trail
	params := NewQuery().
		WithSubject(subject).
		WithPredicate(predicate).
		Build()

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

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

	t.Logf("Query returned %d results", queryResult.TotalCount)

	// Small delay for audit to be written
	time.Sleep(100 * time.Millisecond)

	// Get the agent ID from the client's signer
	agentID := client.signer.PublicKey()

	// Calculate time range (last 1 hour to now)
	now := time.Now().Unix()
	from := now - 3600

	// Trace the agent's queries
	traceParams := TraceParams{
		AgentID: agentID,
		From:    fmt.Sprintf("%d", from),
		To:      fmt.Sprintf("%d", now),
		Subject: subject, // Filter to our test subject
		Limit:   100,
	}

	traceResult, err := client.Trace(ctx, traceParams)
	if err != nil {
		t.Fatalf("Trace() failed: %v", err)
	}

	t.Logf("Trace returned %d audit records", traceResult.TotalCount)

	// Verify we got at least one audit record
	if traceResult.TotalCount == 0 {
		t.Error("Trace() returned no audit records")
	}

	// Verify the trace result has the correct agent ID
	if traceResult.AgentID != agentID {
		t.Errorf("TraceResult.AgentID = %s, want %s", traceResult.AgentID, agentID)
	}

	// Verify the trace result has the correct time range
	if traceResult.FromTimestamp != uint64(from) {
		t.Errorf("TraceResult.FromTimestamp = %d, want %d", traceResult.FromTimestamp, from)
	}

	// Verify audit records contain our query
	foundOurQuery := false
	for _, audit := range traceResult.Audits {
		t.Logf("Audit record: query_id=%s, timestamp=%d, subject=%v, predicate=%v",
			audit.QueryID, audit.Timestamp, audit.Params.Subject, audit.Params.Predicate)

		// Check if this audit is for our query
		if audit.Params.Subject != nil && *audit.Params.Subject == subject &&
			audit.Params.Predicate != nil && *audit.Params.Predicate == predicate {
			foundOurQuery = true

			// Verify agent ID
			if audit.AgentID == nil || *audit.AgentID != agentID {
				t.Errorf("Audit.AgentID = %v, want %s", audit.AgentID, agentID)
			}

			// Verify result hash is present
			if audit.ResultHash == nil {
				t.Error("Audit.ResultHash is nil, expected a result")
			}

			// Verify contributing assertions
			if len(audit.ContributingAssertions) == 0 {
				t.Error("Audit.ContributingAssertions is empty, expected at least one")
			}
		}
	}

	if !foundOurQuery {
		t.Errorf("Did not find audit record for our query (subject=%s, predicate=%s)", subject, predicate)
	}
}

// TestIntegration_Trace_WithPattern tests trace with subject pattern filtering.
func TestIntegration_Trace_WithPattern(t *testing.T) {
	client := getTestClient(t)
	ctx := context.Background()

	// Create unique subjects with a common prefix
	prefix := uniqueSubject("IntegrationTest_TracePattern")
	subject1 := prefix + "_A"
	subject2 := prefix + "_B"
	predicate := "has_value"

	// Create assertions and query them
	for _, subj := range []string{subject1, subject2} {
		assertion := NewAssertion(subj, predicate).
			WithText("test_value").
			WithConfidence(0.9).
			WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
			Build()

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

		time.Sleep(50 * time.Millisecond)

		// Query to create audit trail
		params := NewQuery().WithSubject(subj).WithPredicate(predicate).Build()
		_, err = client.Query(ctx, params)
		if err != nil {
			t.Fatalf("Query() for %s failed: %v", subj, err)
		}
	}

	time.Sleep(100 * time.Millisecond)

	// Trace with wildcard pattern
	agentID := client.signer.PublicKey()
	now := time.Now().Unix()
	from := now - 3600

	traceParams := TraceParams{
		AgentID: agentID,
		From:    fmt.Sprintf("%d", from),
		To:      fmt.Sprintf("%d", now),
		Subject: prefix + "*", // Match both subjects
		Limit:   100,
	}

	traceResult, err := client.Trace(ctx, traceParams)
	if err != nil {
		t.Fatalf("Trace() failed: %v", err)
	}

	t.Logf("Trace with pattern returned %d audit records", traceResult.TotalCount)

	// Should find both queries
	foundSubjects := make(map[string]bool)
	for _, audit := range traceResult.Audits {
		if audit.Params.Subject != nil {
			foundSubjects[*audit.Params.Subject] = true
		}
	}

	if !foundSubjects[subject1] {
		t.Errorf("Did not find audit for subject %s", subject1)
	}

	if !foundSubjects[subject2] {
		t.Errorf("Did not find audit for subject %s", subject2)
	}
}
