package promptcompiler

import (
	"encoding/json"
	"testing"

	"github.com/stretchr/testify/require"

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

func TestControlledTaskPayloadDigestIgnoresDeliveryAttemptIdentity(t *testing.T) {
	first := controlledPayloadTestMessage("msg-a", "turn-a", "2026-07-30T10:00:00Z", "Review the compact prompt.")
	second := controlledPayloadTestMessage("msg-b", "turn-b", "2026-07-31T10:00:00Z", "Review the compact prompt.")

	firstDigest, firstPresent := controlledTaskPayloadIdentity(ConversationBinding{}, first)
	secondDigest, secondPresent := controlledTaskPayloadIdentity(ConversationBinding{}, second)
	require.True(t, firstPresent)
	require.True(t, secondPresent)
	require.Equal(t, firstDigest, secondDigest)
}

func TestControlledTaskPayloadIdentityDistinguishesNoPayload(t *testing.T) {
	digest, present := controlledTaskPayloadIdentity(ConversationBinding{}, types.InboxMessage{})
	require.False(t, present)
	require.Empty(t, digest)
}

func TestControlledTaskPayloadDigestChangesWithTaskPayload(t *testing.T) {
	first := controlledPayloadTestMessage("msg-a", "turn-a", "2026-07-30T10:00:00Z", "Review the compact prompt.")
	second := controlledPayloadTestMessage("msg-b", "turn-b", "2026-07-31T10:00:00Z", "Implement the compact prompt.")

	firstDigest, firstPresent := controlledTaskPayloadIdentity(ConversationBinding{}, first)
	secondDigest, secondPresent := controlledTaskPayloadIdentity(ConversationBinding{}, second)
	require.True(t, firstPresent)
	require.True(t, secondPresent)
	require.NotEqual(t, firstDigest, secondDigest)
}

func TestControlledTaskPayloadDigestIgnoresAttemptAndTaskIDs(t *testing.T) {
	first := controlledPayloadTestMessage("msg-a", "turn-a", "2026-07-30T10:00:00Z", "Review the compact prompt.")
	first.Metadata = map[string]any{"task": map[string]any{
		"taskId": "tsk-a", "channelRef": "#agentteam", "title": "Prompt review",
	}}
	second := controlledPayloadTestMessage("msg-b", "turn-b", "2026-07-31T10:00:00Z", "Review the compact prompt.")
	second.Metadata = map[string]any{"task": map[string]any{
		"taskId": "tsk-b", "channelRef": "#agentteam", "title": "Prompt review",
	}}

	firstDigest, firstPresent := controlledTaskPayloadIdentity(ConversationBinding{}, first)
	secondDigest, secondPresent := controlledTaskPayloadIdentity(ConversationBinding{}, second)
	require.True(t, firstPresent)
	require.True(t, secondPresent)
	require.Equal(t, firstDigest, secondDigest)
}

func TestControlledTaskPayloadDigestFailsClosedWithoutAttachmentContentIdentity(t *testing.T) {
	message := controlledPayloadTestMessage("msg-a", "turn-a", "2026-07-30T10:00:00Z", "Review the attachment.")
	message.AttachmentList = []types.MessageAttachment{{ID: "att-a", Filename: "prompt.md", ContentType: "text/markdown", SizeBytes: 42}}
	digest, present := controlledTaskPayloadIdentity(ConversationBinding{}, message)
	require.True(t, present)
	require.Empty(t, digest)

	threadRoot := controlledPayloadTestMessage("msg-b", "turn-b", "2026-07-30T10:00:00Z", "Review the thread root.")
	threadRoot.Metadata = map[string]any{"replyThreadRoot": map[string]any{
		"messageId": "root-a",
		"content":   "Root content",
		"attachments": []any{map[string]any{
			"id": "att-root", "filename": "root.md", "contentType": "text/markdown", "sizeBytes": 42,
		}},
	}}
	digest, present = controlledTaskPayloadIdentity(ConversationBinding{}, threadRoot)
	require.True(t, present)
	require.Empty(t, digest)
}

func TestControlledTaskPayloadDigestKeepsExplicitTaskContext(t *testing.T) {
	first := controlledPayloadTestMessage("msg-a", "turn-a", "2026-07-30T10:00:00Z", "Review the compact prompt.")
	first.ActiveTaskTarget = types.ActiveTaskTarget{
		Channel: "#agentteam", TaskNumber: 10, Source: "human_selected",
		TaskScope: "#tsk_first", TaskTitle: "Prompt review",
	}
	second := first
	second.ActiveTaskTarget.TaskTitle = "Different review"

	firstDigest, firstPresent := controlledTaskPayloadIdentity(ConversationBinding{}, first)
	secondDigest, secondPresent := controlledTaskPayloadIdentity(ConversationBinding{}, second)
	require.True(t, firstPresent)
	require.True(t, secondPresent)
	require.NotEqual(t, firstDigest, secondDigest)
}

func TestRuntimeEnvelopeDoesNotSerializeTaskPayloadDigest(t *testing.T) {
	envelope := RuntimeInputEnvelope{TaskPayloadPresent: true, TaskPayloadDigest: "private-digest"}

	data, err := json.Marshal(envelope)
	require.NoError(t, err)
	require.NotContains(t, string(data), "private-digest")
	require.NotContains(t, string(data), "taskPayloadPresent")
}

func controlledPayloadTestMessage(messageID, turn, timestamp, content string) types.InboxMessage {
	return types.InboxMessage{
		MessageID:   messageID,
		Target:      "#agentteam:thread",
		ChannelType: "thread",
		AuthorProfile: types.AuthorProfile{
			ActorType:   "human",
			DisplayName: "alice",
		},
		Content:   content,
		Timestamp: timestamp,
	}
}
