package schema

import (
	"encoding/json"
	"testing"

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

func ptrInt(i int) *int { return &i }

func TestToolCallJSONRoundtrip(t *testing.T) {
	tcid := ToolCallId("tc-1")
	status := ToolCallStatusInProgress
	tc := ToolCall{
		ToolCallId: &tcid,
		Title:      "Read file",
		Kind:       ptrToolKind(ToolKindRead),
		Status:     &status,
	}
	data, err := json.Marshal(tc)
	require.NoError(t, err)

	var decoded ToolCall
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.ToolCallId)
	assert.Equal(t, "tc-1", string(*decoded.ToolCallId))
	assert.Equal(t, "Read file", decoded.Title)
	require.NotNil(t, decoded.Kind)
	assert.Equal(t, ToolKindRead, *decoded.Kind)
}

func TestToolCallUpdateRoundtrip(t *testing.T) {
	tcid := ToolCallId("tc-1")
	status := ToolCallStatusCompleted
	update := ToolCallUpdate{
		ToolCallId: &tcid,
		Status:     &status,
		Title:      ptrString("Updated title"),
	}
	data, err := json.Marshal(update)
	require.NoError(t, err)

	var decoded ToolCallUpdate
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.ToolCallId)
	assert.Equal(t, "tc-1", string(*decoded.ToolCallId))
	require.NotNil(t, decoded.Status)
	assert.Equal(t, ToolCallStatusCompleted, *decoded.Status)
	require.NotNil(t, decoded.Title)
	assert.Equal(t, "Updated title", *decoded.Title)
}

func TestToolCallWithContent(t *testing.T) {
	tcid := ToolCallId("tc-2")
	status := ToolCallStatusInProgress
	tc := ToolCall{
		ToolCallId: &tcid,
		Title:      "Edit file",
		Kind:       ptrToolKind(ToolKindEdit),
		Status:     &status,
		Content: []*ToolCallContent{
			{
				Type:    "content",
				Content: &Content{Content: &ContentBlock{Type: "text", Text: &TextContent{Text: "output text"}}},
			},
			{
				Type: "diff",
				Diff: &Diff{Path: "main.go", NewText: "new content"},
			},
		},
	}
	data, err := json.Marshal(tc)
	require.NoError(t, err)

	var decoded ToolCall
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.Len(t, decoded.Content, 2)
	assert.Equal(t, TypeToolCallContentKindContent, decoded.Content[0].Type)
	assert.Equal(t, TypeToolCallContentKindDiff, decoded.Content[1].Type)
	require.NotNil(t, decoded.Content[1].Diff)
	assert.Equal(t, "main.go", decoded.Content[1].Diff.Path)
}

func TestToolCallContentContentRoundtrip(t *testing.T) {
	content := ToolCallContent{
		Type:    "content",
		Content: &Content{Content: &ContentBlock{Type: "text", Text: &TextContent{Text: "hello"}}},
	}
	data, err := json.Marshal(content)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"type":"content"`)

	var decoded ToolCallContent
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, TypeToolCallContentKindContent, decoded.Type)
	require.NotNil(t, decoded.Content)
	require.NotNil(t, decoded.Content.Content)
	require.NotNil(t, decoded.Content.Content.Text)
	assert.Equal(t, "hello", decoded.Content.Content.Text.Text)
}

func TestToolCallContentDiffRoundtrip(t *testing.T) {
	diff := ToolCallContent{
		Type: "diff",
		Diff: &Diff{Path: "file.go", NewText: "new text"},
	}
	data, err := json.Marshal(diff)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"type":"diff"`)

	var decoded ToolCallContent
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, TypeToolCallContentKindDiff, decoded.Type)
	require.NotNil(t, decoded.Diff)
	assert.Equal(t, "file.go", decoded.Diff.Path)
	assert.Equal(t, "new text", decoded.Diff.NewText)
}

func TestToolCallContentTerminalRoundtrip(t *testing.T) {
	term := ToolCallContent{
		Type:     "terminal",
		Terminal: &Terminal{TerminalId: "term-1"},
	}
	data, err := json.Marshal(term)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"type":"terminal"`)

	var decoded ToolCallContent
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, TypeToolCallContentKindTerminal, decoded.Type)
	require.NotNil(t, decoded.Terminal)
	assert.Equal(t, "term-1", decoded.Terminal.TerminalId)
}

func TestToolCallUpdateAllFields(t *testing.T) {
	tcid := ToolCallId("tc-1")
	status := ToolCallStatusInProgress
	kind := ToolKindRead
	update := ToolCallUpdate{
		ToolCallId: &tcid,
		Status:     &status,
		Kind:       &kind,
		Title:      ptrString("Reading file"),
		Content: []*ToolCallContent{
			{
				Type:    "content",
				Content: &Content{Content: &ContentBlock{Type: "text", Text: &TextContent{Text: "progress"}}},
			},
		},
	}
	data, err := json.Marshal(update)
	require.NoError(t, err)

	var decoded ToolCallUpdate
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.ToolCallId)
	assert.Equal(t, "tc-1", string(*decoded.ToolCallId))
	require.NotNil(t, decoded.Status)
	assert.Equal(t, ToolCallStatusInProgress, *decoded.Status)
	require.Len(t, decoded.Content, 1)
}

func TestToolCallWithLocations(t *testing.T) {
	tcid := ToolCallId("tc-3")
	line := 42
	status := ToolCallStatusInProgress
	tc := ToolCall{
		ToolCallId: &tcid,
		Title:      "Search",
		Kind:       ptrToolKind(ToolKindSearch),
		Status:     &status,
		Locations: []*ToolCallLocation{
			{Path: "file.go", Line: &line},
		},
	}
	data, err := json.Marshal(tc)
	require.NoError(t, err)

	var decoded ToolCall
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.Len(t, decoded.Locations, 1)
	assert.Equal(t, "file.go", decoded.Locations[0].Path)
	require.NotNil(t, decoded.Locations[0].Line)
	assert.Equal(t, 42, *decoded.Locations[0].Line)
}
