package run

import (
	"testing"

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

const implicitFailureRuntimePlan = `apiVersion: loop.plan/v1
kind: PlanVersion
input:
  target: {type: string, required: true}
graph:
  entry: {nodeId: notify, port: in}
  nodes:
  - id: notify
    type: send_message
    input:
      target: {type: string, required: true}
    sendMessage:
      target: "{{ input.target }}"
      text: hello
      completion: sent
  - id: success
    type: end
    end: {outcome: succeeded}
  edges:
  - id: completed
    source: {nodeId: notify, port: completed}
    target: {nodeId: success, port: in}
`

func TestImplicitFailPlanPreservesStructuredReducerFailure(t *testing.T) {
	t.Parallel()
	kernel := compilePlanKernel(t, implicitFailureRuntimePlan)
	state, err := kernel.Start(Values{"target": "  "})
	require.NoError(t, err)

	routed, err := kernel.Step(state, reducerNow)
	require.NoError(t, err)
	require.Equal(t, StatusRunning, routed.State.Status)
	require.Len(t, routed.State.Activations, 2)
	source := routed.State.Activations[0]
	require.Equal(t, []string{"failed"}, source.SelectedPorts)
	require.Equal(t, "message.target_invalid", source.Failure.Code)
	require.NotEmpty(t, source.Failure.Message)

	terminalStep, err := kernel.Step(routed.State, reducerNow)
	require.NoError(t, err)
	require.Equal(t, StatusFailed, terminalStep.State.Status)
	require.Equal(t, source.Failure, terminalStep.State.Failure)
	sink := terminalStep.State.Activations[1]
	require.Equal(t, plan.OpcodeFailPlan, kernel.graph.nodes[sink.NodeID].Opcode)
	require.Equal(t, source.ID, sink.ParentActivationID)
	require.Equal(t, ActivationFailed, sink.Status)
	require.Equal(t, source.Failure, sink.Failure)
}

func TestImplicitFailPlanPreservesExternalFailure(t *testing.T) {
	t.Parallel()
	tests := []struct {
		name       string
		completion Completion
		want       *Failure
	}{
		{
			name: "external failure",
			completion: Completion{
				Port: "failed", Output: Values{}, Failure: &Failure{Code: "agent.failed", Message: "no answer"},
			},
			want: &Failure{Code: "agent.failed", Message: "no answer"},
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			kernel := compilePlanKernel(t, implicitFailureRuntimePlan)
			state, err := kernel.Start(Values{"target": "#all"})
			require.NoError(t, err)
			waiting, err := kernel.Step(state, reducerNow)
			require.NoError(t, err)
			require.Len(t, waiting.Intents, 1)

			completion := test.completion
			completion.ActivationID = waiting.Intents[0].ActivationID
			routed, err := kernel.Complete(waiting.State, completion, reducerNow)
			require.NoError(t, err)
			terminal, err := kernel.Step(routed.State, reducerNow)
			require.NoError(t, err)
			require.Equal(t, StatusFailed, terminal.State.Status)
			require.Equal(t, test.want, terminal.State.Failure)
		})
	}
}
