package fanout

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/store/im"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

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

	writer := &workflowScopeEventWriter{}
	unread := &capturingUnreadRuntime{}
	createdAt := time.Date(2026, 8, 7, 10, 0, 0, 0, time.UTC)
	appMessage := message.AppMessage{
		ID:          "msg_workflow_live",
		Seq:         9,
		ChannelID:   "wfs_1",
		ChannelType: message.WorkflowScopeType,
		AuthorProfile: message.AuthorProfile{
			ActorType:   message.AuthorActorTypeHuman,
			MemberID:    "mem_author",
			DisplayName: "Alice",
		},
		Content:   "Update the selected node",
		CreatedAt: createdAt,
	}

	err := NewDeliveryFanout(nil, writer, nil).
		WithUnreadRuntime(unread).
		DeliverCommittedMessage(t.Context(), "wsp_1", appMessage, message.MessageDelivery{
			MessageID: appMessage.ID,
			Seq:       appMessage.Seq,
			Message: message.ReceiveMessage{
				MessageID:   appMessage.ID,
				TargetKey:   appMessage.ChannelID,
				TargetSeq:   appMessage.Seq,
				ChannelType: message.WorkflowScopeType,
			},
		})
	require.NoError(t, err)

	require.Zero(t, writer.durableAppends, "Workflow product messages must not enter the durable member IM stream")
	require.Empty(t, unread.messages, "Workflow product messages do not participate in ordinary Inbox unread")
	require.Len(t, writer.liveEvents, 1)
	live := writer.liveEvents[0]
	require.Equal(t, "wsp_1", live.workspaceID)
	require.Equal(t, "wfs_1", live.scopeID)
	require.Equal(t, "message.created", live.event.Type)
	require.Equal(t, appMessage.ID, live.event.EntityID)
	require.NotNil(t, live.event.EntitySeq)
	require.Equal(t, appMessage.Seq, *live.event.EntitySeq)
	require.Equal(t, createdAt, live.event.CreatedAt)
	require.Equal(t, true, live.event.Payload["suppressBrowserNotification"])
	wireMessage, ok := live.event.Payload["message"].(dto.AppMessage)
	require.True(t, ok)
	require.Equal(t, appMessage.ID, wireMessage.ID)
	require.Equal(t, appMessage.ChannelID, wireMessage.ChannelID)
	require.Equal(t, message.WorkflowScopeType, wireMessage.ChannelType)
	require.NotNil(t, wireMessage.AuthorProfile)
	require.Equal(t, "Alice", wireMessage.AuthorProfile.DisplayName)
}

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

	writer := &durableOnlyWorkflowEventWriter{}
	appMessage := message.AppMessage{
		ID:          "msg_workflow_no_live_publisher",
		Seq:         1,
		ChannelID:   "wfs_1",
		ChannelType: message.WorkflowScopeType,
	}

	require.NoError(t, NewDeliveryFanout(nil, writer, nil).DeliverCommittedMessage(
		t.Context(),
		"wsp_1",
		appMessage,
		message.MessageDelivery{
			MessageID: appMessage.ID,
			Seq:       appMessage.Seq,
			Message: message.ReceiveMessage{
				MessageID:   appMessage.ID,
				TargetKey:   appMessage.ChannelID,
				TargetSeq:   appMessage.Seq,
				ChannelType: message.WorkflowScopeType,
			},
		},
	))
	require.Zero(t, writer.durableAppends, "missing live publication support must fail closed instead of widening into durable IM")
}

type workflowScopeLiveEvent struct {
	workspaceID string
	scopeID     string
	event       im.StreamEvent
}

type workflowScopeEventWriter struct {
	durableAppends int
	liveEvents     []workflowScopeLiveEvent
}

func (writer *workflowScopeEventWriter) AppendScopeEvent(
	context.Context,
	im.ScopeEventRequest,
) ([]im.StreamEvent, error) {
	writer.durableAppends++
	return nil, nil
}

func (writer *workflowScopeEventWriter) PublishScopeLiveOnlyEvent(
	workspaceID string,
	scopeID string,
	event im.StreamEvent,
) {
	writer.liveEvents = append(writer.liveEvents, workflowScopeLiveEvent{
		workspaceID: workspaceID,
		scopeID:     scopeID,
		event:       event,
	})
}

type durableOnlyWorkflowEventWriter struct {
	durableAppends int
}

func (writer *durableOnlyWorkflowEventWriter) AppendScopeEvent(
	context.Context,
	im.ScopeEventRequest,
) ([]im.StreamEvent, error) {
	writer.durableAppends++
	return nil, nil
}
