package aifin

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

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/extension/runner"
	"github.com/tidbcloud/AgentTeam/internal/extension/runner/mcp"
)

func TestNormalizeToolResultPrefersStructuredContent(t *testing.T) {
	t.Parallel()

	got, err := normalizeToolResult(mcp.ToolResult{
		StructuredContent: json.RawMessage(`{"rows":[{"code":"600519.SH"}]}`),
		Content: []json.RawMessage{
			json.RawMessage(`{"type":"image","data":"ignored","mimeType":"image/png"}`),
			json.RawMessage(`{"type":"text","text":"not json"}`),
		},
	})
	require.NoError(t, err)
	require.JSONEq(
		t,
		`{"result":{"rows":[{"code":"600519.SH"}]}}`,
		string(got),
	)
}

func TestNormalizeToolResultAllowsOnlyUniqueJSONTextFallback(t *testing.T) {
	t.Parallel()

	for _, content := range []string{
		`{"type":"text","text":"[1,true,\"ok\"]"}`,
		`{"type":"text","text":"[1,true,\"ok\"]","annotations":{"audience":["user"]},"_meta":{"trace":"opaque"}}`,
	} {
		got, err := normalizeToolResult(mcp.ToolResult{
			Content: []json.RawMessage{json.RawMessage(content)},
		})
		require.NoError(t, err)
		require.JSONEq(t, `{"result":[1,true,"ok"]}`, string(got))
	}
}

func TestNormalizeToolResultFailsClosed(t *testing.T) {
	t.Parallel()

	oversized := `"` +
		strings.Repeat("x", runner.MaxResponseBytes) +
		`"`
	testCases := []struct {
		name   string
		result mcp.ToolResult
	}{
		{
			name: "missing result",
		},
		{
			name: "structured null does not fall back",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(`null`),
				Content: []json.RawMessage{
					json.RawMessage(`{"type":"text","text":"{\"ok\":true}"}`),
				},
			},
		},
		{
			name: "structured whitespace does not fall back",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(" \n\t "),
				Content: []json.RawMessage{
					json.RawMessage(`{"type":"text","text":"{\"ok\":true}"}`),
				},
			},
		},
		{
			name: "wire structured null presence does not fall back",
			result: mcp.ToolResult{
				StructuredContentPresent: true,
				Content: []json.RawMessage{
					json.RawMessage(`{"type":"text","text":"{\"ok\":true}"}`),
				},
			},
		},
		{
			name: "empty fallback text",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(`{"type":"text","text":"  "}`),
			}},
		},
		{
			name: "multiple fallback content",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(`{"type":"text","text":"{}"}`),
				json.RawMessage(`{"type":"text","text":"{}"}`),
			}},
		},
		{
			name: "heterogeneous fallback content",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(`{"type":"image","data":"x","mimeType":"image/png"}`),
			}},
		},
		{
			name: "malformed content item",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(`{"type":"text"`),
			}},
		},
		{
			name: "fallback duplicate content type",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(
					`{"type":"image","type":"text","text":"{\"ok\":true}"}`,
				),
			}},
		},
		{
			name: "fallback non json",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(`{"type":"text","text":"not json"}`),
			}},
		},
		{
			name: "fallback multiple json documents",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(`{"type":"text","text":"{} {}"}`),
			}},
		},
		{
			name: "structured invalid json",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(`{"value":NaN}`),
			},
		},
		{
			name: "fallback unknown content field",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(
					`{"type":"text","text":"{\"rows\":[]}","unknown":true}`,
				),
			}},
		},
		{
			name: "fallback MCP marker field",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(
					`{"type":"text","text":"{\"rows\":[]}","isError":false}`,
				),
			}},
		},
		{
			name: "fallback invalid metadata",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(
					`{"type":"text","text":"{\"rows\":[]}","_meta":"opaque"}`,
				),
			}},
		},
		{
			name: "structured non finite json number",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(`{"value":1e999}`),
			},
		},
		{
			name: "structured invalid UTF-8",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage{
					'{', '"', 'v', 'a', 'l', 'u', 'e', '"', ':',
					'"', 0xff, '"', '}',
				},
			},
		},
		{
			name: "duplicate mcp tool error code",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"mcp_tool_error_code":1001,"mcp_tool_error_code":0}`,
				),
			},
		},
		{
			name: "duplicate is error marker",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"isError":true,"isError":false,"rows":[]}`,
				),
			},
		},
		{
			name: "duplicate structured content marker",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"structuredContent":{"error":"failed"},"structuredContent":{"rows":[]}}`,
				),
			},
		},
		{
			name: "duplicate content marker",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"content":[{"type":"text","text":"failed"}],"content":[]}`,
				),
			},
		},
		{
			name: "duplicate embedded error marker",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"error":{"code":"FAILED"},"error":null,"data":[]}`,
				),
			},
		},
		{
			name: "duplicate nested business code",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"data":{"code":1003,"code":0,"message":"OK","data":[]}}`,
				),
			},
		},
		{
			name: "excessive JSON nesting",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					strings.Repeat("[", 130) +
						"0" +
						strings.Repeat("]", 130),
				),
			},
		},
		{
			name: "wind mcp tool error",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"mcp_tool_error_code":1001,"mcp_tool_error_msg":"failed"}`,
				),
			},
		},
		{
			name: "wind nested error",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"data":null,"error":{"code":"QUERY_FAILED","message":"failed"}}`,
				),
			},
		},
		{
			name: "nested MCP envelope",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"payload":{"isError":true,"content":[{"type":"text","text":"failed"}]}}`,
				),
			},
		},
		{
			name: "nested Wind tool error",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"rows":[{"mcp_tool_error_code":1001}]}`,
				),
			},
		},
		{
			name: "nested embedded error",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"payload":{"error":{"code":"QUERY_FAILED","message":"failed"}}}`,
				),
			},
		},
		{
			name: "structured non-empty input requests",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"inputRequests":{"confirm":{}},"rows":[]}`,
				),
			},
		},
		{
			name: "nested non-empty input requests",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"payload":{"inputRequests":{"confirm":{}}},"rows":[]}`,
				),
			},
		},
		{
			name: "text fallback non-empty input requests",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(
					`{"type":"text","text":"{\"inputRequests\":{\"confirm\":{}},\"rows\":[]}"}`,
				),
			}},
		},
		{
			name: "wind business error",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"data":{"code":1003,"message":"not found","data":[]}}`,
				),
			},
		},
		{
			name: "wind numeric business error without message",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"data":{"code":1003,"rows":[]}}`,
				),
			},
		},
		{
			name: "wind numeric string business error",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"data":{"code":"1003","message":"not found","data":[]}}`,
				),
			},
		},
		{
			name: "wind ambiguous 2xx business status",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"data":{"code":200,"message":"partial","data":[1]}}`,
				),
			},
		},
		{
			name: "embedded is error",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"isError":true,"content":[{"type":"text","text":"failed"}]}`,
				),
			},
		},
		{
			name: "embedded successful mcp content envelope",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"isError":false,"content":[{"type":"text","text":"{\"rows\":[]}"}]}`,
				),
			},
		},
		{
			name: "embedded mcp structured content envelope",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"content":[],"structuredContent":{"rows":[]}}`,
				),
			},
		},
		{
			name: "embedded JSON RPC envelope",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"jsonrpc":"2.0","id":1,"result":{"rows":[]}}`,
				),
			},
		},
		{
			name: "fallback contains mcp envelope",
			result: mcp.ToolResult{Content: []json.RawMessage{
				json.RawMessage(
					`{"type":"text","text":"{\"content\":[{\"type\":\"text\",\"text\":\"nested\"}]}"}`,
				),
			}},
		},
		{
			name: "embedded interactive result",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(
					`{"resultType":"input_required","inputRequests":{"confirm":{}}}`,
				),
			},
		},
		{
			name: "oversized normalized output",
			result: mcp.ToolResult{
				StructuredContent: json.RawMessage(oversized),
			},
		},
	}

	for _, testCase := range testCases {
		testCase := testCase
		t.Run(testCase.name, func(t *testing.T) {
			t.Parallel()

			_, err := normalizeToolResult(testCase.result)
			require.Error(t, err)
			require.Equal(t, runner.ErrorUpstreamFailure, runner.CodeOf(err))
		})
	}
}

func TestNormalizeToolResultAcceptsWindSuccessShapes(t *testing.T) {
	t.Parallel()

	for _, raw := range []string{
		`{"mcp_tool_error_code":0,"data":{"rows":[]}}`,
		`{"error":null,"data":{"rows":[]}}`,
		`{"data":{"code":0,"message":"ok","data":[]}}`,
		`{"data":{"code":200,"message":"OK","data":[]}}`,
		`{"data":{"code":201,"message":"SUCCESS","data":[]}}`,
		`{"data":{"code":"200","message":"SUCCESS","data":[]}}`,
		`{"inputRequests":{},"rows":[]}`,
		`{"rows":[{"code":"600519.SH","content":["price","volume"]}]}`,
		`{"rows":[{"code":"600519.SH","message":"company announcement"}]}`,
		`{"rows":[{"code":"600519.SH","message":"company announcement","data":{"publishedAt":"2026-08-20"}}]}`,
	} {
		got, err := normalizeToolResult(mcp.ToolResult{
			StructuredContent: json.RawMessage(raw),
		})
		require.NoError(t, err)
		require.JSONEq(t, `{"result":`+raw+`}`, string(got))
	}
}
