package anthropic

import (
	"encoding/json"
	"strings"
	"testing"

	"nex/backend/internal/conversion/canonical"

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

func TestStreamEncoder_MessageStart(t *testing.T) {
	e := NewStreamEncoder()
	event := canonical.NewMessageStartEvent("msg_1", "claude-3")

	chunks := e.EncodeEvent(event)
	require.Len(t, chunks, 1)

	s := string(chunks[0])
	assert.True(t, strings.HasPrefix(s, "event: message_start\n"))
	assert.Contains(t, s, "data: ")
	assert.Contains(t, s, "msg_1")
	assert.Contains(t, s, "claude-3")
}

func TestStreamEncoder_ContentBlockDelta(t *testing.T) {
	e := NewStreamEncoder()
	event := canonical.NewContentBlockDeltaEvent(0, canonical.StreamDelta{Type: "text_delta", Text: "你好"})

	chunks := e.EncodeEvent(event)
	require.Len(t, chunks, 1)

	s := string(chunks[0])
	assert.True(t, strings.HasPrefix(s, "event: content_block_delta\n"))
	assert.Contains(t, s, "你好")

	// 验证 JSON 格式
	lines := strings.Split(s, "\n")
	var dataLine string
	for _, l := range lines {
		if strings.HasPrefix(l, "data: ") {
			dataLine = strings.TrimPrefix(l, "data: ")
			break
		}
	}
	var payload map[string]any
	require.NoError(t, json.Unmarshal([]byte(dataLine), &payload))
	assert.Equal(t, "content_block_delta", payload["type"])
}

func TestStreamEncoder_MessageStop(t *testing.T) {
	e := NewStreamEncoder()
	event := canonical.NewMessageStopEvent()

	chunks := e.EncodeEvent(event)
	require.Len(t, chunks, 1)

	s := string(chunks[0])
	assert.True(t, strings.HasPrefix(s, "event: message_stop\n"))
}

func TestStreamEncoder_ContentBlockStart_Text(t *testing.T) {
	e := NewStreamEncoder()
	event := canonical.NewContentBlockStartEvent(0, canonical.StreamContentBlock{Type: "text", Text: ""})

	chunks := e.EncodeEvent(event)
	require.Len(t, chunks, 1)

	s := string(chunks[0])
	assert.True(t, strings.HasPrefix(s, "event: content_block_start\n"))
	assert.Contains(t, s, "data: ")

	var payload map[string]any
	lines := strings.Split(s, "\n")
	for _, l := range lines {
		if strings.HasPrefix(l, "data: ") {
			require.NoError(t, json.Unmarshal([]byte(strings.TrimPrefix(l, "data: ")), &payload))
			break
		}
	}
	cb, ok := payload["content_block"].(map[string]any)
	require.True(t, ok)
	assert.Equal(t, "text", cb["type"])
}

func TestStreamEncoder_ContentBlockStart_ToolUse(t *testing.T) {
	e := NewStreamEncoder()
	event := canonical.NewContentBlockStartEvent(1, canonical.StreamContentBlock{
		Type: "tool_use",
		ID:   "toolu_1",
		Name: "search",
	})

	chunks := e.EncodeEvent(event)
	require.Len(t, chunks, 1)

	s := string(chunks[0])
	assert.Contains(t, s, "toolu_1")
	assert.Contains(t, s, "search")

	var payload map[string]any
	lines := strings.Split(s, "\n")
	for _, l := range lines {
		if strings.HasPrefix(l, "data: ") {
			require.NoError(t, json.Unmarshal([]byte(strings.TrimPrefix(l, "data: ")), &payload))
			break
		}
	}
	cb, ok := payload["content_block"].(map[string]any)
	require.True(t, ok)
	assert.Equal(t, "tool_use", cb["type"])
	assert.Equal(t, "toolu_1", cb["id"])
	assert.Equal(t, "search", cb["name"])
}

func TestStreamEncoder_ContentBlockStart_Thinking(t *testing.T) {
	e := NewStreamEncoder()
	event := canonical.NewContentBlockStartEvent(0, canonical.StreamContentBlock{Type: "thinking", Thinking: ""})

	chunks := e.EncodeEvent(event)
	require.Len(t, chunks, 1)

	s := string(chunks[0])
	assert.Contains(t, s, "thinking")

	var payload map[string]any
	lines := strings.Split(s, "\n")
	for _, l := range lines {
		if strings.HasPrefix(l, "data: ") {
			require.NoError(t, json.Unmarshal([]byte(strings.TrimPrefix(l, "data: ")), &payload))
			break
		}
	}
	cb, ok := payload["content_block"].(map[string]any)
	require.True(t, ok)
	assert.Equal(t, "thinking", cb["type"])
}

func TestStreamEncoder_ContentBlockStop(t *testing.T) {
	e := NewStreamEncoder()
	idx := 2
	event := canonical.CanonicalStreamEvent{
		Type:  canonical.EventContentBlockStop,
		Index: &idx,
	}

	chunks := e.EncodeEvent(event)
	require.Len(t, chunks, 1)

	s := string(chunks[0])
	assert.True(t, strings.HasPrefix(s, "event: content_block_stop\n"))
	assert.Contains(t, s, "content_block_stop")
}

func TestStreamEncoder_MessageDelta_WithStopReason(t *testing.T) {
	e := NewStreamEncoder()
	sr := canonical.StopReasonEndTurn
	event := canonical.CanonicalStreamEvent{
		Type:       canonical.EventMessageDelta,
		StopReason: &sr,
	}

	chunks := e.EncodeEvent(event)
	require.Len(t, chunks, 1)

	s := string(chunks[0])
	assert.Contains(t, s, "stop_reason")

	var payload map[string]any
	lines := strings.Split(s, "\n")
	for _, l := range lines {
		if strings.HasPrefix(l, "data: ") {
			require.NoError(t, json.Unmarshal([]byte(strings.TrimPrefix(l, "data: ")), &payload))
			break
		}
	}
	delta, okd := payload["delta"].(map[string]any)
	require.True(t, okd)
	assert.Equal(t, "end_turn", delta["stop_reason"])
}

func TestStreamEncoder_MessageDelta_WithUsage(t *testing.T) {
	e := NewStreamEncoder()
	usage := canonical.CanonicalUsage{OutputTokens: 88}
	event := canonical.CanonicalStreamEvent{
		Type:  canonical.EventMessageDelta,
		Usage: &usage,
	}

	chunks := e.EncodeEvent(event)
	require.Len(t, chunks, 1)

	s := string(chunks[0])
	assert.Contains(t, s, "output_tokens")

	var payload map[string]any
	lines := strings.Split(s, "\n")
	for _, l := range lines {
		if strings.HasPrefix(l, "data: ") {
			require.NoError(t, json.Unmarshal([]byte(strings.TrimPrefix(l, "data: ")), &payload))
			break
		}
	}
	u, oku := payload["usage"].(map[string]any)
	require.True(t, oku)
	assert.Equal(t, float64(88), u["output_tokens"])
}

func TestStreamEncoder_Ping(t *testing.T) {
	e := NewStreamEncoder()
	event := canonical.NewPingEvent()

	chunks := e.EncodeEvent(event)
	require.Len(t, chunks, 1)

	s := string(chunks[0])
	assert.True(t, strings.HasPrefix(s, "event: ping\n"))
	assert.Contains(t, s, "ping")
}

func TestStreamEncoder_Error(t *testing.T) {
	e := NewStreamEncoder()
	event := canonical.NewErrorEvent("overloaded_error", "服务过载")

	chunks := e.EncodeEvent(event)
	require.Len(t, chunks, 1)

	s := string(chunks[0])
	assert.True(t, strings.HasPrefix(s, "event: error\n"))
	assert.Contains(t, s, "overloaded_error")
	assert.Contains(t, s, "服务过载")
}

func TestStreamEncoder_Flush_ReturnsNil(t *testing.T) {
	e := NewStreamEncoder()
	chunks := e.Flush()
	assert.Nil(t, chunks)
}

func TestStreamEncoder_UnknownEvent_ReturnsNil(t *testing.T) {
	e := NewStreamEncoder()
	event := canonical.CanonicalStreamEvent{Type: "unknown_event_type"}
	chunks := e.EncodeEvent(event)
	assert.Nil(t, chunks)
}
