package plan

import (
	"fmt"
	"strings"
	"testing"

	"github.com/stretchr/testify/require"
)

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

	targets := []struct {
		name   string
		target string
	}{
		{name: "channel", target: "#ops"},
		{name: "channel reply", target: "#ops:abcdef"},
		{name: "dm", target: "dm:@alpha-agent"},
		{name: "dm reply", target: "dm:@alpha-agent:abcdef"},
		{name: "task", target: "#tsk_abc123"},
		{name: "team", target: "#team_abcdefghijklmn"},
		{name: "team reply", target: "#team_abcdefghijklmn:abcdef"},
	}
	for _, test := range targets {
		test := test
		t.Run(test.name, func(t *testing.T) {
			t.Parallel()

			source := strings.Replace(
				implicitFailureSource,
				`target: "#all"`,
				fmt.Sprintf("target: %q", test.target),
				1,
			)
			artifact, diagnostics, err := Compile([]byte(source))
			require.NoError(t, err)
			require.False(t, HasErrors(diagnostics), "diagnostics: %#v", diagnostics)
			require.NotNil(t, artifact)
		})
	}
}

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

	for name, target := range map[string]string{
		"malformed task":      "#tsk_Bad",
		"Workflow edit scope": "workflow:wfs_builder",
	} {
		t.Run(name, func(t *testing.T) {
			source := strings.Replace(
				implicitFailureSource,
				`target: "#all"`,
				fmt.Sprintf("target: %q", target),
				1,
			)
			artifact, diagnostics, err := Compile([]byte(source))
			require.NoError(t, err)
			require.Nil(t, artifact)

			diagnostic, ok := findCode(diagnostics, "send_message.invalid_target")
			require.True(t, ok, "diagnostics: %#v", diagnostics)
			require.Equal(t, "graph.nodes[0].sendMessage.target", diagnostic.Path)
			require.Equal(t, "notify", diagnostic.NodeID)
		})
	}
}

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

	for _, target := range []string{
		`target: "{{ input.worker }}"`,
		`target: "dm:@{{ input.worker }}"`,
		`target: "{{ [input.worker] }}"`,
	} {
		t.Run(target, func(t *testing.T) {
			candidate := strings.Replace(validCallbackPlan, `target: "{{ input.channel }}"`, target, 1)
			candidate = strings.Replace(
				candidate,
				"input:\n  channel: {type: channel, required: true}",
				"input:\n  channel: {type: channel, required: true}\n  worker: {type: agent, required: true}",
				1,
			)
			candidate = strings.Replace(
				candidate,
				"      channel: {ref: plan.input.channel}",
				"      channel: {ref: plan.input.channel}\n      worker: {ref: plan.input.worker}",
				1,
			)
			candidate = strings.Replace(
				candidate,
				"      channel: {type: channel, required: true}",
				"      channel: {type: channel, required: true}\n      worker: {type: agent, required: true}",
				1,
			)
			artifact, diagnostics, err := Compile([]byte(candidate))
			require.NoError(t, err)
			require.Nil(t, artifact)

			diagnostic, ok := findCode(diagnostics, "send_message.target_type")
			require.True(t, ok, "diagnostics: %#v", diagnostics)
			require.Equal(t, "graph.nodes[0].sendMessage.target", diagnostic.Path)
			require.Equal(t, "notify", diagnostic.NodeID)
			require.Contains(t, diagnostic.Message, "sendMessage.responder")
		})
	}
}
