package adk

import (
	"context"
	"encoding/json"
	"testing"

	"github.com/orchard9/stemedb-go/steme"
)

// mockClient implements EpistemeClient for testing
type mockClient struct {
	queryFunc     func(ctx context.Context, params steme.QueryParams) (*steme.QueryResult, error)
	assertFunc    func(ctx context.Context, assertion steme.Assertion) (string, error)
	traceFunc     func(ctx context.Context, params steme.TraceParams) (*steme.TraceResult, error)
	supersedeFunc func(ctx context.Context, params steme.SupersedeParams) (*steme.SupersedeResult, error)
}

func (m *mockClient) Query(ctx context.Context, params steme.QueryParams) (*steme.QueryResult, error) {
	if m.queryFunc != nil {
		return m.queryFunc(ctx, params)
	}
	return &steme.QueryResult{}, nil
}

func (m *mockClient) Assert(ctx context.Context, assertion steme.Assertion) (string, error) {
	if m.assertFunc != nil {
		return m.assertFunc(ctx, assertion)
	}
	return "mock_hash", nil
}

func (m *mockClient) Trace(ctx context.Context, params steme.TraceParams) (*steme.TraceResult, error) {
	if m.traceFunc != nil {
		return m.traceFunc(ctx, params)
	}
	return &steme.TraceResult{}, nil
}

func (m *mockClient) Supersede(ctx context.Context, params steme.SupersedeParams) (*steme.SupersedeResult, error) {
	if m.supersedeFunc != nil {
		return m.supersedeFunc(ctx, params)
	}
	return &steme.SupersedeResult{
		Status:           "superseded",
		TargetHash:       params.TargetHash,
		SupersessionType: params.SupersessionType,
		Timestamp:        1704067200,
	}, nil
}

func TestQueryTool(t *testing.T) {
	// Mock client that returns a test assertion
	client := &mockClient{
		queryFunc: func(ctx context.Context, params steme.QueryParams) (*steme.QueryResult, error) {
			lifecycle := steme.LifecycleApproved
			return &steme.QueryResult{
				Assertions: []steme.AssertionResponse{
					{
						Hash:       "test_hash",
						Subject:    "Tesla_Inc",
						Predicate:  "has_revenue",
						Object:     steme.NewNumberValue(96.7),
						Confidence: 0.95,
						SourceHash: "source_hash",
						Lifecycle:  lifecycle,
					},
				},
				TotalCount: 1,
				HasMore:    false,
			}, nil
		},
	}

	tool := NewQueryTool(client)

	// Test basic query
	input := QueryInput{
		Subject:   "Tesla_Inc",
		Predicate: "has_revenue",
		Lens:      "consensus",
	}

	inputBytes, err := json.Marshal(input)
	if err != nil {
		t.Fatalf("failed to marshal input: %v", err)
	}

	outputBytes, err := tool.Execute(context.Background(), inputBytes)
	if err != nil {
		t.Fatalf("query failed: %v", err)
	}

	var output QueryOutput
	if err := json.Unmarshal(outputBytes, &output); err != nil {
		t.Fatalf("failed to unmarshal output: %v", err)
	}

	// Verify output
	if output.Confidence != 0.95 {
		t.Errorf("expected confidence 0.95, got %f", output.Confidence)
	}

	if output.Lifecycle != "Approved" {
		t.Errorf("expected lifecycle Approved, got %s", output.Lifecycle)
	}

	if output.QueryID == "" {
		t.Error("expected non-empty query ID")
	}

	if len(output.Sources) == 0 {
		t.Error("expected at least one source")
	}
}

func TestQueryToolConfidenceThreshold(t *testing.T) {
	// Mock client that returns low confidence
	client := &mockClient{
		queryFunc: func(ctx context.Context, params steme.QueryParams) (*steme.QueryResult, error) {
			lifecycle := steme.LifecycleApproved
			return &steme.QueryResult{
				Assertions: []steme.AssertionResponse{
					{
						Hash:       "test_hash",
						Subject:    "test",
						Predicate:  "test",
						Object:     steme.NewTextValue("test"),
						Confidence: 0.6, // Low confidence
						SourceHash: "source_hash",
						Lifecycle:  lifecycle,
					},
				},
				TotalCount: 1,
			}, nil
		},
	}

	tool := NewQueryTool(client)

	input := QueryInput{
		Subject:       "test",
		Predicate:     "test",
		MinConfidence: 0.8, // Require high confidence
	}

	inputBytes, _ := json.Marshal(input)
	outputBytes, err := tool.Execute(context.Background(), inputBytes)
	if err != nil {
		t.Fatalf("query failed: %v", err)
	}

	var output QueryOutput
	if err := json.Unmarshal(outputBytes, &output); err != nil {
		t.Fatalf("failed to unmarshal output: %v", err)
	}

	// Should have error about low confidence
	if output.Error == "" {
		t.Error("expected error for low confidence")
	}
}

func TestAssertTool(t *testing.T) {
	// Track what assertion was created
	var capturedAssertion steme.Assertion

	client := &mockClient{
		assertFunc: func(ctx context.Context, assertion steme.Assertion) (string, error) {
			capturedAssertion = assertion
			return "created_hash", nil
		},
	}

	tool := NewAssertTool(client)

	input := AssertInput{
		Subject:    "Tesla_Inc",
		Predicate:  "has_revenue",
		Object:     96.7,
		SourceHash: "0000000000000000000000000000000000000000000000000000000000000000",
		Confidence: 0.95,
		Lifecycle:  "approved",
	}

	inputBytes, _ := json.Marshal(input)
	outputBytes, err := tool.Execute(context.Background(), inputBytes)
	if err != nil {
		t.Fatalf("assert failed: %v", err)
	}

	var output AssertOutput
	if err := json.Unmarshal(outputBytes, &output); err != nil {
		t.Fatalf("failed to unmarshal output: %v", err)
	}

	// Verify output
	if !output.Success {
		t.Errorf("expected success, got error: %s", output.Error)
	}

	if output.Hash != "created_hash" {
		t.Errorf("expected hash created_hash, got %s", output.Hash)
	}

	// Verify captured assertion
	if capturedAssertion.Subject != "Tesla_Inc" {
		t.Errorf("expected subject Tesla_Inc, got %s", capturedAssertion.Subject)
	}

	// Use approximate comparison for floating point
	if capturedAssertion.Confidence < 0.94 || capturedAssertion.Confidence > 0.96 {
		t.Errorf("expected confidence ~0.95, got %f", capturedAssertion.Confidence)
	}
}
