package runner_test

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

	"github.com/stretchr/testify/require"

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

// TestFormatIntegerQueryValue locks the plain-decimal re-encoding of every schema-legal
// integer lexical: exponent and decimal forms render as plain decimal
// digits, integers up to the explicit query budget stay exact (no float64
// rounding or safe-integer bound), and fractional, oversized, or invalid
// lexicals fail closed.
func TestFormatIntegerQueryValue(t *testing.T) {
	t.Parallel()

	cases := []struct {
		lexical string
		want    string
	}{
		{lexical: "10", want: "10"},
		{lexical: "-10", want: "-10"},
		{lexical: "0", want: "0"},
		{lexical: "1e1", want: "10"},
		{lexical: "1E1", want: "10"},
		{lexical: "1.0", want: "1"},
		{lexical: "1.5e1", want: "15"},
		{lexical: "-0.0", want: "0"},
		{lexical: "9007199254740993.0", want: "9007199254740993"},
		{lexical: "1e18", want: "1000000000000000000"},
		{lexical: "2288457142539548672053", want: "2288457142539548672053"},
	}
	for _, testCase := range cases {
		formatted, err := runner.FormatIntegerQueryValue(json.Number(testCase.lexical))
		require.NoErrorf(t, err, "lexical %q", testCase.lexical)
		require.Equalf(t, testCase.want, formatted, "lexical %q", testCase.lexical)
	}

	for _, lexical := range []string{
		"1.5", "-1.5", "1e-1", "0.1", "abc", "", "1.2.3",
		// Near-miss lexicals strconv and big.Rat would accept but the JSON
		// number grammar rejects.
		"01", "+1", "-01", "0x10", "1_0", "1/1", "1 ", " 1", "1.",
		// The lexical-work and normalized-decimal budgets fail closed before
		// query construction.
		strings.Repeat("9", 129), strings.Repeat("9", 257),
	} {
		_, err := runner.FormatIntegerQueryValue(json.Number(lexical))
		require.Errorf(t, err, "lexical %q must fail closed", lexical)
		require.Equal(t, runner.ErrorValidation, runner.CodeOf(err))
	}
}

func TestFormatIntegerQueryValueRejectsResourceAmplifyingExponents(t *testing.T) {
	for _, lexical := range []string{"1e1000000", "1e-1000000"} {
		t.Run(lexical, func(t *testing.T) {
			_, err := runner.FormatIntegerQueryValue(json.Number(lexical))
			require.Error(t, err)
			require.Equal(t, runner.ErrorValidation, runner.CodeOf(err))
		})
	}
}

// TestFormatIntegerQueryValuesInPlace locks the in-place rewrite: nil and empty numbers
// pass through, formatted values replace the originals, and one bad lexical
// aborts before any later field is touched.
func TestFormatIntegerQueryValuesInPlace(t *testing.T) {
	t.Parallel()

	empty := json.Number("")
	pageNum := json.Number("1e1")
	pageSize := json.Number("2.0")
	require.NoError(t, runner.FormatIntegerQueryValuesInPlace(&pageNum, &pageSize, nil, &empty))
	require.Equal(t, json.Number("10"), pageNum)
	require.Equal(t, json.Number("2"), pageSize)
	require.Equal(t, json.Number(""), empty)

	fractional := json.Number("1.5")
	following := json.Number("2e1")
	err := runner.FormatIntegerQueryValuesInPlace(&fractional, &following)
	require.Equal(t, runner.ErrorValidation, runner.CodeOf(err))
	require.Equal(t, json.Number("2e1"), following)
}
