package promptcompiler

import (
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
)

func TestFormatPromptMessageMalformedWorkflowSourceKeepsHumanAuthor(t *testing.T) {
	// A workflow source missing the effect id does not satisfy the durable
	// identity contract, so the compiled prompt must keep the Human author and
	// must not manufacture a Workflow identity.
	message := types.InboxMessage{
		MessageID: "msg-workflow-callback",
		Target:    "#agentteam",
		AuthorProfile: types.AuthorProfile{
			ActorType:   "human",
			PrincipalID: "usr_owner",
			MemberID:    "mem_owner",
			DisplayName: "Arithmetic E2E Owner",
		},
		Source: types.ContributionSource{
			Kind:    "workflow_run",
			RefType: "workflow_run",
			RefID:   "wfr_calculator",
			RunID:   "wfr_calculator",
			Label:   "Parallel calculator",
		},
		Content: "Compute the next work item.",
	}

	prompt := FormatPromptMessage("#agentteam", message, "message_send")
	require.Contains(t, prompt, "Arithmetic E2E Owner")
	require.NotContains(t, prompt, "Parallel calculator")
	require.NotContains(t, prompt, "@Parallel")
}

func TestFormatPromptMessage_WorkflowReplyThreadRootShowsAutomationAuthor(t *testing.T) {
	// The server projects a canonical Workflow service author on the
	// reply-thread-root metadata, so the compiled inbound prompt must show the
	// Workflow, never the frozen Human authorization/audit principal.
	message := types.InboxMessage{
		MessageID:     "msg-workflow-root",
		TargetSeq:     2,
		AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "wink"},
		Content:       "@codex1 please continue",
		Metadata: map[string]any{
			"replyThreadRoot": map[string]any{
				"messageId":  "root-workflow",
				"authorType": "service",
				"authorName": "Parallel calculator",
				"authorProfile": map[string]any{
					"actorType":   "service",
					"displayName": "Parallel calculator",
					"serviceKind": "workflow",
				},
				"content":   "Compute 23 + 19.",
				"timestamp": "2026-07-31T15:30:00Z",
			},
		},
	}

	prompt := FormatPromptMessage("#agentteam:00000034", message, "message_send")

	require.Contains(t, prompt, "thread root:")
	require.Contains(t, prompt, "@Parallel calculator:")
	require.NotContains(t, prompt, "Arithmetic E2E Owner")
	require.NotContains(t, prompt, "@Arithmetic")
}

func TestFormatPromptMessage_ScheduleReplyThreadRootShowsAutomationAuthor(t *testing.T) {
	message := types.InboxMessage{
		MessageID:     "msg-schedule-root",
		TargetSeq:     2,
		AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "wink"},
		Content:       "@codex1 please continue",
		Metadata: map[string]any{
			"replyThreadRoot": map[string]any{
				"messageId":  "root-schedule",
				"authorType": "service",
				"authorName": "Daily standup",
				"authorProfile": map[string]any{
					"actorType":   "service",
					"displayName": "Daily standup",
					"serviceKind": "schedule",
				},
				"content":   "Standup check-in.",
				"timestamp": "2026-07-31T15:30:00Z",
			},
		},
	}

	prompt := FormatPromptMessage("#agentteam:00000034", message, "message_send")

	require.Contains(t, prompt, "thread root:")
	require.Contains(t, prompt, "@Daily standup:")
	require.NotContains(t, prompt, "Arithmetic E2E Owner")
}

func TestFormatPromptMessage_LegacyWorkflowReplyThreadRootSourceFallback(t *testing.T) {
	// Old-server compatibility: the root carries a Human profile plus a trusted
	// workflow source that satisfies the durable identity contract. The Desktop
	// legacy fallback derives the recognizable automation author, never the Human.
	message := types.InboxMessage{
		MessageID:     "msg-legacy-root",
		TargetSeq:     2,
		AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "wink"},
		Content:       "@codex1 please continue",
		Metadata: map[string]any{
			"replyThreadRoot": map[string]any{
				"messageId":  "root-legacy",
				"authorType": "human",
				"authorName": "Arithmetic E2E Owner",
				"authorProfile": map[string]any{
					"actorType":   "human",
					"displayName": "Arithmetic E2E Owner",
				},
				"source": map[string]any{
					"kind":    "workflow_run",
					"refType": "workflow_run",
					"refId":   "wfr_calculator",
					"runId":   "wfr_calculator",
					"eventId": "wrf_calculator_effect",
					"label":   "Parallel calculator",
				},
				"content":   "Compute 23 + 19.",
				"timestamp": "2026-07-31T15:30:00Z",
			},
		},
	}

	prompt := FormatPromptMessage("#agentteam:00000034", message, "message_send")

	require.Contains(t, prompt, "thread root:")
	require.Contains(t, prompt, "Parallel calculator")
	require.NotContains(t, prompt, "Arithmetic E2E Owner")
	require.NotContains(t, prompt, "@Arithmetic")
}

func TestFormatPromptMessage_MalformedWorkflowReplyThreadRootKeepsHumanAuthor(t *testing.T) {
	// A malformed workflow source (missing effect id / ref id) must fail closed:
	// the Human remains the reply-thread-root author and no Workflow identity is
	// manufactured.
	for _, source := range []map[string]any{
		{"kind": "workflow_run", "refType": "workflow_run", "refId": "wfr_calculator", "runId": "wfr_calculator", "label": "Parallel calculator"},
		{"kind": "workflow_run", "refType": "workflow_run", "runId": "run_1", "eventId": "wrf_1", "label": "Parallel calculator"},
	} {
		message := types.InboxMessage{
			MessageID:     "msg-malformed-root",
			TargetSeq:     2,
			AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "wink"},
			Content:       "@codex1 please continue",
			Metadata: map[string]any{
				"replyThreadRoot": map[string]any{
					"messageId":  "root-malformed",
					"authorType": "human",
					"authorName": "Arithmetic E2E Owner",
					"authorProfile": map[string]any{
						"actorType":   "human",
						"displayName": "Arithmetic E2E Owner",
					},
					"source":    source,
					"content":   "Compute 23 + 19.",
					"timestamp": "2026-07-31T15:30:00Z",
				},
			},
		}

		prompt := FormatPromptMessage("#agentteam:00000034", message, "message_send")

		require.Contains(t, prompt, "thread root:")
		require.Contains(t, prompt, "Arithmetic E2E Owner")
		require.NotContains(t, prompt, "Parallel calculator")
		require.NotContains(t, prompt, "@Parallel")
	}
}

func TestFormatPromptMessage_MalformedScheduleReplyThreadRootKeepsHumanAuthor(t *testing.T) {
	// A malformed schedule source (missing ref id / ref run mismatch) must fail
	// closed: the Human remains the reply-thread-root author and no Scheduled
	// identity is manufactured.
	for _, source := range []map[string]any{
		{"kind": "schedule_run", "refType": "automation_run", "runId": "run_2", "label": "Daily standup"},
		{"kind": "schedule_run", "refType": "automation_run", "refId": "run_2", "runId": "run_other", "label": "Daily standup"},
	} {
		message := types.InboxMessage{
			MessageID:     "msg-malformed-schedule-root",
			TargetSeq:     2,
			AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "wink"},
			Content:       "@codex1 please continue",
			Metadata: map[string]any{
				"replyThreadRoot": map[string]any{
					"messageId":  "root-malformed-schedule",
					"authorType": "human",
					"authorName": "Arithmetic E2E Owner",
					"authorProfile": map[string]any{
						"actorType":   "human",
						"displayName": "Arithmetic E2E Owner",
					},
					"source":    source,
					"content":   "Daily check-in.",
					"timestamp": "2026-07-31T15:30:00Z",
				},
			},
		}

		prompt := FormatPromptMessage("#agentteam:00000034", message, "message_send")

		require.Contains(t, prompt, "thread root:")
		require.Contains(t, prompt, "Arithmetic E2E Owner")
		require.NotContains(t, prompt, "Daily standup")
		require.NotContains(t, prompt, "@Daily")
	}
}
