package schema

import (
	"encoding/json"
	"testing"

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

func ptrString(s string) *string { return &s }
func ptrRole(r Role) *Role       { return &r }

func TestNewTextBlock(t *testing.T) {
	block := ContentBlock{Type: "text", Text: &TextContent{Text: "hello world"}}
	assert.Equal(t, TypeContentBlockKindText, block.Type)
	require.NotNil(t, block.Text)
	assert.Equal(t, "hello world", block.Text.Text)
}

func TestTextBlockJSONRoundtrip(t *testing.T) {
	block := ContentBlock{Type: "text", Text: &TextContent{Text: "hello"}}
	data, err := json.Marshal(block)
	require.NoError(t, err)

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

func TestTextBlockOmitsOptionalFields(t *testing.T) {
	block := ContentBlock{Type: "text", Text: &TextContent{Text: "hello"}}
	data, err := json.Marshal(block)
	require.NoError(t, err)
	assert.NotContains(t, string(data), "annotations")
	assert.NotContains(t, string(data), "_meta")
}

func TestImageBlockJSONRoundtrip(t *testing.T) {
	block := ContentBlock{Type: "image", Image: &ImageContent{Data: "base64data", MimeType: "image/png"}}
	data, err := json.Marshal(block)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"type":"image"`)

	var decoded ContentBlock
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, TypeContentBlockKindImage, decoded.Type)
	require.NotNil(t, decoded.Image)
	assert.Equal(t, "base64data", decoded.Image.Data)
	assert.Equal(t, "image/png", decoded.Image.MimeType)
}

func TestAudioBlockJSONRoundtrip(t *testing.T) {
	block := ContentBlock{Type: "audio", Audio: &AudioContent{Data: "base64audio", MimeType: "audio/mp3"}}
	data, err := json.Marshal(block)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"type":"audio"`)

	var decoded ContentBlock
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, TypeContentBlockKindAudio, decoded.Type)
	require.NotNil(t, decoded.Audio)
	assert.Equal(t, "base64audio", decoded.Audio.Data)
}

func TestResourceLinkBlockJSONRoundtrip(t *testing.T) {
	block := ContentBlock{Type: "resource_link", Resource_link: &ResourceLink{Name: "readme", Uri: "file:///README.md"}}
	data, err := json.Marshal(block)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"type":"resource_link"`)

	var decoded ContentBlock
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, TypeContentBlockKindResource_link, decoded.Type)
	require.NotNil(t, decoded.Resource_link)
	assert.Equal(t, "readme", decoded.Resource_link.Name)
	assert.Equal(t, "file:///README.md", decoded.Resource_link.Uri)
}

func TestEmbeddedTextResourceRoundtrip(t *testing.T) {
	block := ContentBlock{
		Type: "resource",
		Resource: &EmbeddedResource{
			Resource: &EmbeddedResourceResource{
				Type:                 "textresourcecontents",
				TextResourceContents: &TextResourceContents{Uri: "file:///main.go", MimeType: ptrString("text/plain"), Text: "file content"},
			},
		},
	}
	data, err := json.Marshal(block)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"type":"resource"`)

	var decoded ContentBlock
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, TypeContentBlockKindResource, decoded.Type)
	require.NotNil(t, decoded.Resource)
	require.NotNil(t, decoded.Resource.Resource)
	require.NotNil(t, decoded.Resource.Resource.TextResourceContents)
	assert.Equal(t, "file content", decoded.Resource.Resource.TextResourceContents.Text)
	assert.Equal(t, "file:///main.go", decoded.Resource.Resource.TextResourceContents.Uri)
}

func TestEmbeddedBlobResourceRoundtrip(t *testing.T) {
	block := ContentBlock{
		Type: "resource",
		Resource: &EmbeddedResource{
			Resource: &EmbeddedResourceResource{
				Type:                 "blobresourcecontents",
				BlobResourceContents: &BlobResourceContents{Uri: "file:///image.png", MimeType: ptrString("image/png"), Blob: "blobdata"},
			},
		},
	}
	data, err := json.Marshal(block)
	require.NoError(t, err)

	var decoded ContentBlock
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.Resource)
	require.NotNil(t, decoded.Resource.Resource)
	require.NotNil(t, decoded.Resource.Resource.BlobResourceContents)
	assert.Equal(t, "blobdata", decoded.Resource.Resource.BlobResourceContents.Blob)
}

func TestContentBlockUnmarshalMissingType(t *testing.T) {
	var block ContentBlock
	err := json.Unmarshal([]byte(`{"text":"hello"}`), &block)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "missing type field")
}

func TestImageContentWithURI(t *testing.T) {
	uri := "https://example.com/image.png"
	block := ContentBlock{
		Type:  "image",
		Image: &ImageContent{Data: "data", MimeType: "image/png", Uri: &uri},
	}
	data, err := json.Marshal(block)
	require.NoError(t, err)

	var decoded ContentBlock
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.Image)
	require.NotNil(t, decoded.Image.Uri)
	assert.Equal(t, uri, *decoded.Image.Uri)
}

func TestAnnotationsJSON(t *testing.T) {
	priority := 0.8
	ann := Annotations{
		Audience: []*Role{ptrRole(RoleUser)},
		Priority: &priority,
	}
	data, err := json.Marshal(ann)
	require.NoError(t, err)

	var decoded Annotations
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.Len(t, decoded.Audience, 1)
	assert.Equal(t, RoleUser, *decoded.Audience[0])
	require.NotNil(t, decoded.Priority)
	assert.Equal(t, 0.8, *decoded.Priority)
}

func TestContentBlockArrayRoundtrip(t *testing.T) {
	priority := 1.0
	blocks := []ContentBlock{
		{Type: "text", Text: &TextContent{Text: "hello"}},
		{Type: "image", Image: &ImageContent{Data: "imgdata", MimeType: "image/png"}},
		{Type: "resource_link", Resource_link: &ResourceLink{Name: "file", Uri: "file:///x"}},
		{
			Type: "text",
			Text: &TextContent{
				Text: "annotated",
				Annotations: &Annotations{
					Audience: []*Role{ptrRole(RoleAssistant)},
					Priority: &priority,
				},
			},
		},
	}
	data, err := json.Marshal(blocks)
	require.NoError(t, err)

	var decoded []ContentBlock
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.Len(t, decoded, 4)
	assert.Equal(t, TypeContentBlockKindText, decoded[0].Type)
	assert.Equal(t, TypeContentBlockKindImage, decoded[1].Type)
	assert.Equal(t, TypeContentBlockKindResource_link, decoded[2].Type)
	assert.Equal(t, TypeContentBlockKindText, decoded[3].Type)
	require.NotNil(t, decoded[3].Text)
	require.NotNil(t, decoded[3].Text.Annotations)
	require.Len(t, decoded[3].Text.Annotations.Audience, 1)
	assert.Equal(t, RoleAssistant, *decoded[3].Text.Annotations.Audience[0])
}
