package schema

import (
	"encoding/json"
	"testing"

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

func ptrVers(v int) *ProtocolVersion                          { pv := ProtocolVersion(v); return &pv }
func ptrSessionId(s string) *SessionId                        { sid := SessionId(s); return &sid }
func ptrStopReason(s StopReason) *StopReason                  { return &s }
func ptrOptId(s string) *PermissionOptionId                   { o := PermissionOptionId(s); return &o }
func ptrOptKind(k PermissionOptionKind) *PermissionOptionKind { return &k }

func TestInitializeRequestRoundtrip(t *testing.T) {
	req := InitializeRequest{
		ProtocolVersion:    ptrVers(1),
		ClientCapabilities: &ClientCapabilities{},
		ClientInfo:         &Implementation{Name: "test-client", Version: "1.0.0"},
	}
	data, err := json.Marshal(req)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"protocolVersion":1`)

	var decoded InitializeRequest
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.ProtocolVersion)
	assert.Equal(t, 1, int(*decoded.ProtocolVersion))
	require.NotNil(t, decoded.ClientInfo)
	assert.Equal(t, "test-client", decoded.ClientInfo.Name)
}

func TestInitializeResponseRoundtrip(t *testing.T) {
	resp := InitializeResponse{
		ProtocolVersion:   ptrVers(1),
		AgentCapabilities: &AgentCapabilities{},
		AgentInfo:         &Implementation{Name: "test-agent", Version: "0.1.0"},
	}
	data, err := json.Marshal(resp)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"protocolVersion":1`)

	var decoded InitializeResponse
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.ProtocolVersion)
	assert.Equal(t, 1, int(*decoded.ProtocolVersion))
	require.NotNil(t, decoded.AgentInfo)
	assert.Equal(t, "test-agent", decoded.AgentInfo.Name)
}

func TestNewSessionRequestRoundtrip(t *testing.T) {
	req := NewSessionRequest{
		Cwd:        "/tmp",
		McpServers: []*McpServer{},
	}
	data, err := json.Marshal(req)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"cwd":"/tmp"`)
	assert.Contains(t, string(data), `"mcpServers":[]`)

	var decoded NewSessionRequest
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, "/tmp", decoded.Cwd)
}

func TestPromptRequestRoundtrip(t *testing.T) {
	block := ContentBlock{Type: "text", Text: &TextContent{Text: "hello"}}
	req := PromptRequest{
		SessionId: ptrSessionId("session-1"),
		Prompt:    []*ContentBlock{&block},
	}
	data, err := json.Marshal(req)
	require.NoError(t, err)

	var decoded PromptRequest
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.SessionId)
	assert.Equal(t, "session-1", string(*decoded.SessionId))
	require.Len(t, decoded.Prompt, 1)
	require.NotNil(t, decoded.Prompt[0].Text)
	assert.Equal(t, "hello", decoded.Prompt[0].Text.Text)
}

func TestPromptResponseRoundtrip(t *testing.T) {
	resp := PromptResponse{
		StopReason: ptrStopReason(StopReasonEndTurn),
	}
	data, err := json.Marshal(resp)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"stopReason":"end_turn"`)

	var decoded PromptResponse
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.StopReason)
	assert.Equal(t, StopReasonEndTurn, *decoded.StopReason)
}

func TestCancelNotificationJSON(t *testing.T) {
	notif := CancelNotification{SessionId: ptrSessionId("sess-123")}
	data, err := json.Marshal(notif)
	require.NoError(t, err)

	var decoded CancelNotification
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.SessionId)
	assert.Equal(t, "sess-123", string(*decoded.SessionId))
}

func TestSessionNotificationJSON(t *testing.T) {
	update := SessionUpdate{
		SessionUpdate:     "agent_message_chunk",
		AgentMessageChunk: &ContentChunk{Content: &ContentBlock{Type: "text", Text: &TextContent{Text: "hello"}}},
	}
	notif := SessionNotification{
		SessionId: ptrSessionId("session-1"),
		Update:    &update,
	}
	data, err := json.Marshal(notif)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"sessionUpdate":"agent_message_chunk"`)

	var decoded SessionNotification
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.SessionId)
	assert.Equal(t, "session-1", string(*decoded.SessionId))
	assert.Equal(t, SessionUpdateKindAgentMessageChunk, decoded.Update.SessionUpdate)
	require.NotNil(t, decoded.Update.AgentMessageChunk)
	require.NotNil(t, decoded.Update.AgentMessageChunk.Content)
	require.NotNil(t, decoded.Update.AgentMessageChunk.Content.Text)
	assert.Equal(t, "hello", decoded.Update.AgentMessageChunk.Content.Text.Text)
}

func TestPermissionOptionRoundtrip(t *testing.T) {
	opt := PermissionOption{
		OptionId: ptrOptId("opt-1"),
		Name:     "Allow once",
		Kind:     ptrOptKind(PermissionOptionKindAllowOnce),
	}
	data, err := json.Marshal(opt)
	require.NoError(t, err)

	var decoded PermissionOption
	require.NoError(t, json.Unmarshal(data, &decoded))
	require.NotNil(t, decoded.OptionId)
	assert.Equal(t, "opt-1", string(*decoded.OptionId))
	require.NotNil(t, decoded.Kind)
	assert.Equal(t, PermissionOptionKindAllowOnce, *decoded.Kind)
}

func TestReadTextFileRequestRoundtrip(t *testing.T) {
	line := 10
	limit := 5
	req := ReadTextFileRequest{
		SessionId: ptrSessionId("sess-1"),
		Path:      "file.go",
		Line:      &line,
		Limit:     &limit,
	}
	data, err := json.Marshal(req)
	require.NoError(t, err)

	var decoded ReadTextFileRequest
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, "file.go", decoded.Path)
	require.NotNil(t, decoded.Line)
	assert.Equal(t, 10, *decoded.Line)
}

func TestCreateTerminalRequestRoundtrip(t *testing.T) {
	req := CreateTerminalRequest{
		SessionId: ptrSessionId("sess-1"),
		Command:   "bash",
		Args:      []string{"-c", "echo hello"},
	}
	data, err := json.Marshal(req)
	require.NoError(t, err)

	var decoded CreateTerminalRequest
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, "bash", decoded.Command)
	require.Len(t, decoded.Args, 2)
}
