package aifin

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

	"github.com/stretchr/testify/require"

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

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

	testCases := []struct {
		name   string
		action string
		input  string
		want   map[string]any
	}{
		{
			name:   "stock price",
			action: ActionGetStockPriceIndicators,
			input:  `{"windcode":" 00700.hk ,600519.sh ","indexes":"最新成交价,涨跌幅"}`,
			want: map[string]any{
				"windcode": "0700.HK,600519.SH",
				"indexes":  "最新成交价,涨跌幅",
			},
		},
		{
			name:   "fund price omits indexes",
			action: ActionGetFundPriceIndicators,
			input:  `{"windcode":"005827.of"}`,
			want:   map[string]any{"windcode": "005827.OF"},
		},
		{
			name:   "index price",
			action: ActionGetIndexPriceIndicators,
			input:  `{"windcode":"000300.sh"}`,
			want:   map[string]any{"windcode": "000300.SH"},
		},
		{
			name:   "stock kline defaults and maps period",
			action: ActionGetStockKline,
			input:  `{"windcode":"600519.sh","begin_date":"2026-01-01","end_date":"2026-01-31"}`,
			want: map[string]any{
				"windcode":   "600519.SH",
				"begin_date": "2026-01-01",
				"end_date":   "2026-01-31",
				"period":     "10",
			},
		},
		{
			name:   "fund kline maps all optionals",
			action: ActionGetFundKline,
			input:  `{"windcode":"588200.sh","begin_date":"2026-01-01","end_date":"2026-01-31","period":"1w","aftype":"1","issusp":"0","afdate":"2026-01-15"}`,
			want: map[string]any{
				"windcode":   "588200.SH",
				"begin_date": "2026-01-01",
				"end_date":   "2026-01-31",
				"period":     "11",
				"aftype":     "1",
				"issusp":     "0",
				"afdate":     "2026-01-15",
			},
		},
		{
			name:   "index kline maps minute period",
			action: ActionGetIndexKline,
			input:  `{"windcode":"000300.SH","begin_date":"2026-01-01","end_date":"2026-01-02","period":"5min"}`,
			want: map[string]any{
				"windcode":   "000300.SH",
				"begin_date": "2026-01-01",
				"end_date":   "2026-01-02",
				"period":     "3",
			},
		},
		{
			name:   "stock question",
			action: ActionSearchStocks,
			input:  `{"question":"筛选沪深市场市值超500亿的股票"}`,
			want:   map[string]any{"question": "筛选沪深市场市值超500亿的股票"},
		},
		{
			name:   "stock fundamentals question",
			action: ActionGetStockFundamentals,
			input:  `{"question":"贵州茅台2025年净利润"}`,
			want:   map[string]any{"question": "贵州茅台2025年净利润"},
		},
		{
			name:   "fund question",
			action: ActionSearchFunds,
			input:  `{"question":"筛选近一年收益率超过20%的股票型基金"}`,
			want:   map[string]any{"question": "筛选近一年收益率超过20%的股票型基金"},
		},
		{
			name:   "fund performance question",
			action: ActionGetFundPerformance,
			input:  `{"question":"易方达蓝筹精选近一年最大回撤"}`,
			want:   map[string]any{"question": "易方达蓝筹精选近一年最大回撤"},
		},
		{
			name:   "announcements",
			action: ActionGetCompanyAnnouncements,
			input:  `{"query":"贵州茅台2025年年度报告","top_k":3}`,
			want:   map[string]any{"query": "贵州茅台2025年年度报告", "top_k": 3},
		},
		{
			name:   "news omits top k",
			action: ActionGetFinancialNews,
			input:  `{"query":"近一周美联储利率政策新闻"}`,
			want:   map[string]any{"query": "近一周美联储利率政策新闻"},
		},
		{
			name:   "economic chinese mode",
			action: ActionNaturalLanguageGetEDBData,
			input:  `{"executionMode":"搜索并提数","question":"中国GDP","observation":"10"}`,
			want: map[string]any{
				"executionMode": "searchFetch",
				"question":      "中国GDP",
				"observation":   "10",
			},
		},
		{
			name:   "economic date range",
			action: ActionNaturalLanguageGetEDBData,
			input:  `{"executionMode":"fetch","question":"G0000069","beginDate":"2025-01-01","endDate":"2025-12-31"}`,
			want: map[string]any{
				"executionMode": "fetch",
				"question":      "G0000069",
				"beginDate":     "2025-01-01",
				"endDate":       "2025-12-31",
			},
		},
	}

	validators, err := compileCatalogValidators(Definition())
	require.NoError(t, err)
	for _, testCase := range testCases {
		testCase := testCase
		t.Run(testCase.name, func(t *testing.T) {
			t.Parallel()

			input, err := validators.validateInput(
				testCase.action,
				json.RawMessage(testCase.input),
			)
			require.NoError(t, err)
			got, err := mapToolArguments(testCase.action, input)
			require.NoError(t, err)
			require.Equal(t, testCase.want, got)
		})
	}
}

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

	codes := make([]string, 51)
	for index := range codes {
		codes[index] = "600519.SH"
	}
	tooManyCodes, err := json.Marshal(map[string]any{
		"windcode": strings.Join(codes, ","),
	})
	require.NoError(t, err)

	testCases := []struct {
		name   string
		action string
		input  json.RawMessage
	}{
		{
			name:   "unknown action",
			action: "call-tool",
			input:  json.RawMessage(`{}`),
		},
		{
			name:   "extra field",
			action: ActionSearchStocks,
			input:  json.RawMessage(`{"question":"A股","toolName":"search_stocks"}`),
		},
		{
			name:   "whitespace question",
			action: ActionSearchStocks,
			input:  json.RawMessage(`{"question":"   "}`),
		},
		{
			name:   "fractional top k",
			action: ActionGetFinancialNews,
			input:  json.RawMessage(`{"query":"新闻","top_k":1.5}`),
		},
		{
			name:   "high precision fractional top k",
			action: ActionGetFinancialNews,
			input:  json.RawMessage(`{"query":"新闻","top_k":1.0000000000000000000001}`),
		},
		{
			name:   "empty wind code component",
			action: ActionGetStockPriceIndicators,
			input:  json.RawMessage(`{"windcode":"600519.SH,,000300.SH"}`),
		},
		{
			name:   "trailing empty wind code component",
			action: ActionGetStockPriceIndicators,
			input:  json.RawMessage(`{"windcode":"600519.SH,"}`),
		},
		{
			name:   "too many wind codes",
			action: ActionGetStockPriceIndicators,
			input:  tooManyCodes,
		},
		{
			name:   "empty comma-separated wind codes",
			action: ActionGetStockPriceIndicators,
			input:  json.RawMessage(`{"windcode":" , "}`),
		},
		{
			name:   "kline date order",
			action: ActionGetStockKline,
			input:  json.RawMessage(`{"windcode":"600519.SH","begin_date":"2026-02-01","end_date":"2026-01-01"}`),
		},
		{
			name:   "kline invalid date",
			action: ActionGetStockKline,
			input:  json.RawMessage(`{"windcode":"600519.SH","begin_date":"2026-13-40","end_date":"2026-01-01"}`),
		},
		{
			name:   "economic incomplete dates",
			action: ActionNaturalLanguageGetEDBData,
			input:  json.RawMessage(`{"executionMode":"fetch","question":"G0000069","beginDate":"2025-01-01"}`),
		},
		{
			name:   "economic observation and dates",
			action: ActionNaturalLanguageGetEDBData,
			input:  json.RawMessage(`{"executionMode":"fetch","question":"G0000069","beginDate":"2025-01-01","endDate":"2025-12-31","observation":"10"}`),
		},
		{
			name:   "economic fetch lacks range",
			action: ActionNaturalLanguageGetEDBData,
			input:  json.RawMessage(`{"executionMode":"fetch","question":"G0000069"}`),
		},
	}

	validators, err := compileCatalogValidators(Definition())
	require.NoError(t, err)
	for _, testCase := range testCases {
		testCase := testCase
		t.Run(testCase.name, func(t *testing.T) {
			t.Parallel()

			input, validateErr := validators.validateInput(
				testCase.action,
				testCase.input,
			)
			if validateErr == nil {
				_, validateErr = mapToolArguments(testCase.action, input)
			}
			require.Error(t, validateErr)
			if testCase.name == "unknown action" {
				require.Equal(t, runner.ErrorNotFound, runner.CodeOf(validateErr))
			} else {
				require.Equal(t, runner.ErrorValidation, runner.CodeOf(validateErr))
			}
		})
	}
}
