package run

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

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/AgentTeam/internal/workflow/plan"
)

func TestCompiledExpressionAndTemplateEvaluation(t *testing.T) {
	t.Parallel()
	_, graph := goldenKernel(t, "send_message_callback.json")
	node, ok := graph.Node("notify")
	require.True(t, ok)
	context := EvalContext{ActivationInput: Values{"channel": "#workflow"}}
	require.NotNil(t, node.Config.SendMessage.Target)
	target, err := EvalTemplate(*node.Config.SendMessage.Target, context)
	require.NoError(t, err)
	require.Equal(t, "#workflow", target)
	text, err := EvalTemplate(node.Config.SendMessage.Text, context)
	require.NoError(t, err)
	require.Equal(t, "@agent answer in 9 characters or more", text)
}

func TestBorrowedFanOutExpressionBudgetsIntermediateCompositeValues(t *testing.T) {
	t.Parallel()
	large := strings.Repeat("x", 2200<<10)
	context := EvalContext{ActivationInput: Values{"large": large}}
	load := plan.ArtifactExpression{
		Op: plan.ArtifactOpLoad, ResultType: plan.TypeString,
		Load: &plan.ArtifactLoad{
			Space: plan.ArtifactLoadActivationInput,
			Path:  []string{"large"},
		},
	}
	repeated := plan.ArtifactExpression{
		Op: plan.ArtifactOpArray, ResultType: plan.TypeArray,
		Args: []plan.ArtifactExpression{load, load},
	}

	t.Run("length cannot hide an oversized repeated operand", func(t *testing.T) {
		_, err := evalExpressionBorrowed(plan.ArtifactExpression{
			Op: plan.ArtifactOpLength, ResultType: plan.TypeNumber,
			Args: []plan.ArtifactExpression{repeated},
		}, context)
		require.Error(t, err)
		require.True(t, isFanOutResourceLimit(err), err)
	})

	t.Run("in cannot hide an oversized repeated haystack", func(t *testing.T) {
		_, err := evalExpressionBorrowed(plan.ArtifactExpression{
			Op: plan.ArtifactOpIn, ResultType: plan.TypeBoolean,
			Args: []plan.ArtifactExpression{load, repeated},
		}, context)
		require.Error(t, err)
		require.True(t, isFanOutResourceLimit(err), err)
	})

	t.Run("binary operands are budgeted together before equality", func(t *testing.T) {
		_, err := evalExpressionBorrowed(plan.ArtifactExpression{
			Op: plan.ArtifactOpEqual, ResultType: plan.TypeBoolean,
			Args: []plan.ArtifactExpression{load, load},
		}, context)
		require.Error(t, err)
		require.True(t, isFanOutResourceLimit(err), err)
	})
}

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

	template := plan.ArtifactTemplate{Segments: []plan.ArtifactTemplateSegment{
		{Kind: plan.ArtifactTemplateSegmentText, Text: "work scope: "},
		{
			Kind: plan.ArtifactTemplateSegmentExpr,
			Expression: &plan.ArtifactExpression{
				Op:         plan.ArtifactOpLoad,
				ResultType: plan.TypeScope,
				Load: &plan.ArtifactLoad{
					Space: plan.ArtifactLoadActivationInput,
					Path:  []string{"work_scope"},
				},
			},
		},
	}}
	rendered, err := EvalTemplate(template, EvalContext{ActivationInput: Values{
		"work_scope": map[string]any{
			"scopeId": "tsk_abcdefghijklmn",
			"kind":    "task",
		},
	}})
	require.NoError(t, err)
	require.Equal(t, `work scope: {"kind":"task","scopeId":"tsk_abcdefghijklmn"}`, rendered)
}

func TestCompiledExpressionOperatorsAndScopeLoads(t *testing.T) {
	t.Parallel()
	number := func(value string) plan.ArtifactExpression {
		return plan.ArtifactExpression{Op: plan.ArtifactOpConst, ResultType: plan.TypeNumber, Value: plan.ArtifactLiteral(value)}
	}
	expression := plan.ArtifactExpression{Op: plan.ArtifactOpAnd, ResultType: plan.TypeBoolean, Args: []plan.ArtifactExpression{
		{Op: plan.ArtifactOpGreater, ResultType: plan.TypeBoolean, Args: []plan.ArtifactExpression{number("1.50"), number("1.5")}},
		{Op: plan.ArtifactOpContains, ResultType: plan.TypeBoolean, Args: []plan.ArtifactExpression{
			{Op: plan.ArtifactOpArray, ResultType: plan.TypeArray, Args: []plan.ArtifactExpression{number("2"), number("3")}}, number("3.0"),
		}},
	}}
	value, err := EvalExpression(expression, EvalContext{})
	require.NoError(t, err)
	require.Equal(t, false, value) // short-circuits before the true RHS

	load := plan.ArtifactExpression{Op: plan.ArtifactOpLoad, ResultType: plan.TypeString, Load: &plan.ArtifactLoad{
		Space: plan.ArtifactLoadJoinWinnerValue, ScopeOwnerID: "fan", Path: []string{"answer"},
	}}
	value, err = EvalExpression(load, EvalContext{Joins: map[string]JoinValues{"fan": {WinnerValue: Values{"answer": "ok"}}}})
	require.NoError(t, err)
	require.Equal(t, "ok", value)
	wholeBranch := plan.ArtifactExpression{Op: plan.ArtifactOpLoad, ResultType: plan.TypeObject, Load: &plan.ArtifactLoad{
		Space: plan.ArtifactLoadJoinBranch, ScopeOwnerID: "fan", Branch: "left", Path: []string{},
	}}
	value, err = EvalExpression(wholeBranch, EvalContext{Joins: map[string]JoinValues{"fan": {Branches: map[string]Values{"left": {"answer": "ok"}}}}})
	require.NoError(t, err)
	require.Equal(t, Values{"answer": "ok"}, value)

	length := plan.ArtifactExpression{Op: plan.ArtifactOpLength, ResultType: plan.TypeNumber, Args: []plan.ArtifactExpression{{
		Op: plan.ArtifactOpConst, ResultType: plan.TypeString, Value: plan.ArtifactLiteral(`"你好"`),
	}}}
	value, err = EvalExpression(length, EvalContext{})
	require.NoError(t, err)
	require.Equal(t, json.Number("2"), value)
}
