package schema

import (
	"encoding/json"
	"testing"

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

func TestImplementationJSON(t *testing.T) {
	impl := Implementation{
		Name:    "test-agent",
		Version: "1.0.0",
	}
	data, err := json.Marshal(impl)
	require.NoError(t, err)

	var decoded Implementation
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, "test-agent", decoded.Name)
	assert.Equal(t, "1.0.0", decoded.Version)
}

func TestMcpServerStdioRoundtrip(t *testing.T) {
	server := McpServer{Type: TypeMcpServerKindMcpServerStdio, McpServerStdio: &McpServerStdio{
		Name:    "test-server",
		Command: "node",
		Args:    []string{"server.js"},
	}}
	data, err := json.Marshal(server)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"type":"stdio"`)

	var decoded McpServer
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, TypeMcpServerKindMcpServerStdio, decoded.Type)
	require.NotNil(t, decoded.McpServerStdio)
	assert.Equal(t, "test-server", decoded.McpServerStdio.Name)
	assert.Equal(t, "node", decoded.McpServerStdio.Command)
}

func TestMcpServerHttpRoundtrip(t *testing.T) {
	server := McpServer{Type: TypeMcpServerKindMcpServerHttp, McpServerHttp: &McpServerHttp{
		Name: "remote-server",
		Url:  "http://localhost:8080",
	}}
	data, err := json.Marshal(server)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"type":"http"`)

	var decoded McpServer
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, TypeMcpServerKindMcpServerHttp, decoded.Type)
	require.NotNil(t, decoded.McpServerHttp)
	assert.Equal(t, "remote-server", decoded.McpServerHttp.Name)
	assert.Equal(t, "http://localhost:8080", decoded.McpServerHttp.Url)
}

func TestMcpServerSseRoundtrip(t *testing.T) {
	server := McpServer{Type: TypeMcpServerKindMcpServerSse, McpServerSse: &McpServerSse{
		Name: "sse-server",
		Url:  "http://localhost:9090/sse",
	}}
	data, err := json.Marshal(server)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"type":"sse"`)

	var decoded McpServer
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, TypeMcpServerKindMcpServerSse, decoded.Type)
	require.NotNil(t, decoded.McpServerSse)
	assert.Equal(t, "sse-server", decoded.McpServerSse.Name)
}

func TestMcpServerUnmarshalMissingType(t *testing.T) {
	var server McpServer
	err := json.Unmarshal([]byte(`{"name":"test"}`), &server)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "missing type field")
}

func TestAuthMethodAgentRoundtrip(t *testing.T) {
	auth := AuthMethod{Type: TypeAuthMethodKindAuthMethodAgent, AuthMethodAgent: &AuthMethodAgent{
		Id:   "agent-auth",
		Name: "Agent Auth",
	}}
	data, err := json.Marshal(auth)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"type":"agent"`)

	var decoded AuthMethod
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, TypeAuthMethodKindAuthMethodAgent, decoded.Type)
	require.NotNil(t, decoded.AuthMethodAgent)
	assert.Equal(t, "agent-auth", decoded.AuthMethodAgent.Id)
	assert.Equal(t, "Agent Auth", decoded.AuthMethodAgent.Name)
}

func TestDiffJSON(t *testing.T) {
	diff := Diff{
		Path:    "file.go",
		OldText: ptrString("old"),
		NewText: "new",
	}
	data, err := json.Marshal(diff)
	require.NoError(t, err)

	var decoded Diff
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, "file.go", decoded.Path)
	assert.Equal(t, "new", decoded.NewText)
	require.NotNil(t, decoded.OldText)
	assert.Equal(t, "old", *decoded.OldText)
}

func TestStopReasonConstants(t *testing.T) {
	assert.Equal(t, StopReason("end_turn"), StopReasonEndTurn)
	assert.Equal(t, StopReason("max_tokens"), StopReasonMaxTokens)
	assert.Equal(t, StopReason("cancelled"), StopReasonCancelled)
}

func TestToolKindConstants(t *testing.T) {
	kinds := []ToolKind{
		ToolKindRead, ToolKindEdit, ToolKindDelete, ToolKindMove,
		ToolKindSearch, ToolKindExecute, ToolKindThink, ToolKindFetch,
		ToolKindSwitchMode, ToolKindOther,
	}
	for _, k := range kinds {
		assert.NotEmpty(t, string(k))
	}
}

func TestErrorJSONRoundtrip(t *testing.T) {
	code := ErrorCode(42)
	dataMap := map[string]string{"key": "value"}
	errStruct := Error{
		Code:    &code,
		Message: "test error",
		Data:    dataMap,
	}
	data, merr := json.Marshal(errStruct)
	require.NoError(t, merr)

	var decoded Error
	require.NoError(t, json.Unmarshal(data, &decoded))
	assert.Equal(t, "test error", decoded.Message)
	require.NotNil(t, decoded.Code)
	assert.Equal(t, float64(42), *decoded.Code)
}

func TestPlanEntryStatusConstants(t *testing.T) {
	assert.Equal(t, PlanEntryStatus("pending"), PlanEntryStatusPending)
	assert.Equal(t, PlanEntryStatus("in_progress"), PlanEntryStatusInProgress)
	assert.Equal(t, PlanEntryStatus("completed"), PlanEntryStatusCompleted)
}

func TestPlanEntryPriorityConstants(t *testing.T) {
	assert.Equal(t, PlanEntryPriority("high"), PlanEntryPriorityHigh)
	assert.Equal(t, PlanEntryPriority("medium"), PlanEntryPriorityMedium)
	assert.Equal(t, PlanEntryPriority("low"), PlanEntryPriorityLow)
}
