package contrib_test

import (
	"testing"

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

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

func ptrSessionId(s string) *schema.SessionId                          { sid := schema.SessionId(s); return &sid }
func ptrToolKind(k schema.ToolKind) *schema.ToolKind                   { return &k }
func ptrToolCallStatus(s schema.ToolCallStatus) *schema.ToolCallStatus { return &s }

func TestSessionAccumulatorApply(t *testing.T) {
	acc := contrib.NewSessionAccumulator()

	snap, err := acc.Apply(&schema.SessionNotification{
		SessionId: ptrSessionId("sess-1"),
		Update: &schema.SessionUpdate{
			SessionUpdate:     "agent_message_chunk",
			AgentMessageChunk: &schema.ContentChunk{Content: &schema.ContentBlock{Type: "text", Text: &schema.TextContent{Text: "hello"}}},
		},
	})
	require.NoError(t, err)
	assert.Equal(t, "sess-1", snap.SessionID)
	require.Len(t, snap.AgentMessages, 1)
}

func TestSessionAccumulatorAutoReset(t *testing.T) {
	acc := contrib.NewSessionAccumulator()

	_, err := acc.Apply(&schema.SessionNotification{
		SessionId: ptrSessionId("sess-1"),
		Update: &schema.SessionUpdate{
			SessionUpdate:     "agent_message_chunk",
			AgentMessageChunk: &schema.ContentChunk{Content: &schema.ContentBlock{Type: "text", Text: &schema.TextContent{Text: "hello"}}},
		},
	})
	require.NoError(t, err)

	snap, err := acc.Apply(&schema.SessionNotification{
		SessionId: ptrSessionId("sess-2"),
		Update: &schema.SessionUpdate{
			SessionUpdate:     "agent_message_chunk",
			AgentMessageChunk: &schema.ContentChunk{Content: &schema.ContentBlock{Type: "text", Text: &schema.TextContent{Text: "world"}}},
		},
	})
	require.NoError(t, err)
	assert.Equal(t, "sess-2", snap.SessionID)
	require.Len(t, snap.AgentMessages, 1) // reset happened
}

func TestSessionAccumulatorNoAutoReset(t *testing.T) {
	acc := contrib.NewSessionAccumulator(contrib.AutoResetOnSessionChange(false))

	_, err := acc.Apply(&schema.SessionNotification{
		SessionId: ptrSessionId("sess-1"),
		Update: &schema.SessionUpdate{
			SessionUpdate:     "agent_message_chunk",
			AgentMessageChunk: &schema.ContentChunk{Content: &schema.ContentBlock{Type: "text", Text: &schema.TextContent{Text: "hello"}}},
		},
	})
	require.NoError(t, err)

	_, err = acc.Apply(&schema.SessionNotification{
		SessionId: ptrSessionId("sess-2"),
		Update: &schema.SessionUpdate{
			SessionUpdate:     "agent_message_chunk",
			AgentMessageChunk: &schema.ContentChunk{Content: &schema.ContentBlock{Type: "text", Text: &schema.TextContent{Text: "world"}}},
		},
	})
	require.Error(t, err)
	assert.Contains(t, err.Error(), "session mismatch")
}

func TestSessionAccumulatorSubscribe(t *testing.T) {
	acc := contrib.NewSessionAccumulator()

	var snapshots []contrib.SessionSnapshot
	unsub := acc.Subscribe(func(snap contrib.SessionSnapshot, _ schema.SessionNotification) {
		snapshots = append(snapshots, snap)
	})

	_, err := acc.Apply(&schema.SessionNotification{
		SessionId: ptrSessionId("sess-1"),
		Update: &schema.SessionUpdate{
			SessionUpdate:     "agent_message_chunk",
			AgentMessageChunk: &schema.ContentChunk{Content: &schema.ContentBlock{Type: "text", Text: &schema.TextContent{Text: "hello"}}},
		},
	})
	require.NoError(t, err)
	require.Len(t, snapshots, 1)
	assert.Equal(t, "sess-1", snapshots[0].SessionID)

	unsub()

	_, err = acc.Apply(&schema.SessionNotification{
		SessionId: ptrSessionId("sess-1"),
		Update: &schema.SessionUpdate{
			SessionUpdate:     "agent_message_chunk",
			AgentMessageChunk: &schema.ContentChunk{Content: &schema.ContentBlock{Type: "text", Text: &schema.TextContent{Text: "world"}}},
		},
	})
	require.NoError(t, err)
	// No new callback after unsubscribe
	require.Len(t, snapshots, 1)
}

func TestSessionAccumulatorToolCalls(t *testing.T) {
	acc := contrib.NewSessionAccumulator()

	tcid := schema.ToolCallId("tc-1")
	status := schema.ToolCallStatusInProgress
	tc := schema.ToolCall{
		ToolCallId: &tcid,
		Title:      "Read file",
		Kind:       ptrToolKind(schema.ToolKindRead),
		Status:     &status,
	}
	snap, err := acc.Apply(&schema.SessionNotification{
		SessionId: ptrSessionId("sess-1"),
		Update: &schema.SessionUpdate{
			SessionUpdate: "tool_call",
			ToolCall:      &tc,
		},
	})
	require.NoError(t, err)
	require.Len(t, snap.ToolCalls, 1)
	assert.Equal(t, "Read file", snap.ToolCalls["tc-1"].Title)
}

func TestSessionAccumulatorReset(t *testing.T) {
	acc := contrib.NewSessionAccumulator()

	_, err := acc.Apply(&schema.SessionNotification{
		SessionId: ptrSessionId("sess-1"),
		Update: &schema.SessionUpdate{
			SessionUpdate:     "agent_message_chunk",
			AgentMessageChunk: &schema.ContentChunk{Content: &schema.ContentBlock{Type: "text", Text: &schema.TextContent{Text: "hello"}}},
		},
	})
	require.NoError(t, err)

	acc.Reset()

	_, err = acc.Snapshot()
	require.Error(t, err)
}
