package contrib

import (
	"encoding/json"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/keepmind9/acp-sdk-go/schema"
)

func TestToolCallTrackerStart(t *testing.T) {
	tracker := NewToolCallTracker(func() string { return "tc-1" })

	update := tracker.Start("ext-1", "Read file",
		WithKind(schema.ToolKindRead),
		WithStatus(schema.ToolCallStatusInProgress),
	)
	assert.Equal(t, schema.SessionUpdateKindToolCall, update.SessionUpdate)
	require.NotNil(t, update.ToolCall)
	require.NotNil(t, update.ToolCall.ToolCallId)
	assert.Equal(t, "tc-1", string(*update.ToolCall.ToolCallId))
	assert.Equal(t, "Read file", update.ToolCall.Title)
	require.NotNil(t, update.ToolCall.Kind)
	assert.Equal(t, schema.ToolKindRead, *update.ToolCall.Kind)
}

func TestToolCallTrackerProgress(t *testing.T) {
	tracker := NewToolCallTracker(func() string { return "tc-1" })
	tracker.Start("ext-1", "Read file")

	content := []*schema.ToolCallContent{
		{Type: "content", Content: &schema.Content{Content: &schema.ContentBlock{Type: "text", Text: &schema.TextContent{Text: "file contents here"}}}},
	}
	update, err := tracker.Progress("ext-1",
		WithStatus(schema.ToolCallStatusCompleted),
		WithContent(content),
	)
	require.NoError(t, err)
	assert.Equal(t, schema.SessionUpdateKindToolCallUpdate, update.SessionUpdate)
}

func TestToolCallTrackerProgressUnknown(t *testing.T) {
	tracker := NewToolCallTracker(nil)
	_, err := tracker.Progress("nonexistent")
	require.Error(t, err)
	assert.Contains(t, err.Error(), "unknown tool call")
}

func TestToolCallTrackerAppendStreamText(t *testing.T) {
	tracker := NewToolCallTracker(func() string { return "tc-1" })
	tracker.Start("ext-1", "Shell command")

	update1, err := tracker.AppendStreamText("ext-1", "hello ")
	require.NoError(t, err)
	assert.Equal(t, schema.SessionUpdateKindToolCallUpdate, update1.SessionUpdate)

	update2, err := tracker.AppendStreamText("ext-1", "world")
	require.NoError(t, err)
	assert.Equal(t, schema.SessionUpdateKindToolCallUpdate, update2.SessionUpdate)
}

func TestToolCallTrackerForget(t *testing.T) {
	tracker := NewToolCallTracker(func() string { return "tc-1" })
	tracker.Start("ext-1", "Read file")
	tracker.Forget("ext-1")

	_, err := tracker.Progress("ext-1")
	require.Error(t, err)
}

func TestToolCallTrackerRawInputOutput(t *testing.T) {
	tracker := NewToolCallTracker(func() string { return "tc-1" })
	tracker.Start("ext-1", "Edit file",
		WithRawInput(json.RawMessage(`{"path":"main.go"}`)),
		WithRawOutput(json.RawMessage(`{"success":true}`)),
	)

	tc, err := tracker.ToolCallUpdate("ext-1")
	require.NoError(t, err)
	require.NotNil(t, tc.RawInput)
	rawInput, _ := tc.RawInput.(json.RawMessage)
	require.NotNil(t, tc.RawOutput)
	rawOutput, _ := tc.RawOutput.(json.RawMessage)
	assert.JSONEq(t, `{"path":"main.go"}`, string(rawInput))
	assert.JSONEq(t, `{"success":true}`, string(rawOutput))
}

func TestPermissionBrokerRequest(t *testing.T) {
	tracker := NewToolCallTracker(func() string { return "tc-1" })
	tracker.Start("ext-1", "Delete file", WithKind(schema.ToolKindEdit))

	var receivedReq *schema.RequestPermissionRequest
	broker := NewPermissionBroker(
		"sess-1",
		func(req *schema.RequestPermissionRequest) (*schema.RequestPermissionResponse, error) {
			receivedReq = req
			return &schema.RequestPermissionResponse{
				Outcome: ptrOutcome(schema.RequestPermissionOutcomeSelected),
			}, nil
		},
		WithTracker(tracker),
	)

	resp, err := broker.RequestFor("ext-1")
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.NotNil(t, receivedReq)
	require.NotNil(t, receivedReq.SessionId)
	assert.Equal(t, "sess-1", string(*receivedReq.SessionId))
	require.Len(t, receivedReq.Options, 3)
}
