package message

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	plan := scopeApplierPlanForTest(now)
	result, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.NoError(t, err)
	require.True(t, result.Message.Inserted)

	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_messages", "id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_message_sources", "message_id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_message_search_docs", "message_id = $1 AND content = $2", plan.MessageWrite.ID, plan.MessageWrite.Content))
	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_memberships", "scope_id = $1 AND member_id = $2", "scope-applier-thread", "mem-applier-private"))
	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_notification_settings", "scope_id = $1 AND member_id = $2", "scope-applier-thread", "mem-applier-private"))
	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_fanout_outbox", "message_id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_sync_dirty", "workspace_id = $1 AND scope_id = $2 AND latest_message_seq = $3", plan.MessageWrite.WorkspaceID, plan.MessageWrite.ScopeID, plan.MessageWrite.Seq))
	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "member_scope_sync_dirty", "scope_id = $1 AND reason = $2", plan.MessageWrite.ScopeID, "message"))

	var latestSeq, latestGlobalSeq int64
	require.NoError(t, db.QueryRow(ctx, `
SELECT latest_seq, latest_global_seq
FROM scopes
WHERE id = $1
`, "scope-applier-thread").Scan(&latestSeq, &latestGlobalSeq))
	require.Equal(t, plan.MessageWrite.Seq, latestSeq)
	require.Equal(t, plan.MessageWrite.WorkspaceGlobalSeq, latestGlobalSeq)
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	plan := scopeApplierPlanForTest(now)
	first, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.NoError(t, err)
	require.True(t, first.Message.Inserted)

	duplicate := plan
	duplicate.MessageWrite.ID = "msg-applier-duplicate"
	duplicate.MessageWrite.Seq = plan.MessageWrite.Seq + 100
	duplicate.MessageWrite.WorkspaceGlobalSeq = plan.MessageWrite.WorkspaceGlobalSeq + 100
	second, err := applyMessagePlanTxForTest(t, ctx, db, duplicate)
	require.NoError(t, err)
	require.False(t, second.Message.Inserted)
	require.Equal(t, plan.MessageWrite.ID, second.Message.Message.ID)
	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_messages", "client_message_id = $1", plan.MessageWrite.ClientMessageID))
	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_message_search_docs", "message_id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_fanout_outbox", "message_id = $1", plan.MessageWrite.ID))

	var latestSeq, latestGlobalSeq int64
	require.NoError(t, db.QueryRow(ctx, `
SELECT latest_seq, latest_global_seq
FROM scopes
WHERE id = $1
`, "scope-applier-thread").Scan(&latestSeq, &latestGlobalSeq))
	require.Equal(t, plan.MessageWrite.Seq, latestSeq)
	require.Equal(t, plan.MessageWrite.WorkspaceGlobalSeq, latestGlobalSeq)
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	plan := scopeApplierPlanForTest(now)
	first, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.NoError(t, err)
	require.True(t, first.Message.Inserted)

	count, last := loadScopeVisibleSummaryForTest(t, ctx, db, plan.MessageWrite.ScopeID)
	require.EqualValues(t, 1, count)
	require.NotNil(t, last)
	require.Equal(t, plan.MessageWrite.CreatedAt, *last)

	duplicate := plan
	duplicate.MessageWrite.ID = "msg-applier-duplicate-visible"
	duplicate.MessageWrite.Seq = plan.MessageWrite.Seq + 100
	duplicate.MessageWrite.WorkspaceGlobalSeq = plan.MessageWrite.WorkspaceGlobalSeq + 100
	second, err := applyMessagePlanTxForTest(t, ctx, db, duplicate)
	require.NoError(t, err)
	require.False(t, second.Message.Inserted)

	count, last = loadScopeVisibleSummaryForTest(t, ctx, db, plan.MessageWrite.ScopeID)
	require.EqualValues(t, 1, count)
	require.NotNil(t, last)
	require.Equal(t, plan.MessageWrite.CreatedAt, *last)

	hidden := scopeApplierPlanForTest(now.Add(time.Minute))
	hidden.MessageWrite.ID = "msg-applier-hidden"
	hidden.MessageWrite.ClientMessageID = "client-applier-hidden"
	hidden.MessageWrite.Seq = plan.MessageWrite.Seq + 1
	hidden.MessageWrite.WorkspaceGlobalSeq = plan.MessageWrite.WorkspaceGlobalSeq + 1
	hidden.MessageWrite.MessageType = MessageTypeTaskDispatch
	hidden.SourceWrites = nil
	hidden.MembershipMutations = nil
	hidden.NotificationMutations = nil
	hidden.OutboxEvents = nil
	hidden.SideEffectProfile = MessagePlanSideEffectProfileNoOutbox
	hiddenResult, err := applyMessagePlanTxForTest(t, ctx, db, hidden)
	require.NoError(t, err)
	require.True(t, hiddenResult.Message.Inserted)

	count, last = loadScopeVisibleSummaryForTest(t, ctx, db, plan.MessageWrite.ScopeID)
	require.EqualValues(t, 1, count)
	require.NotNil(t, last)
	require.Equal(t, plan.MessageWrite.CreatedAt, *last)
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	memberIDs := []string{
		"mem-applier-owner",
		"mem-applier-reply-b",
		"mem-applier-reply-c",
		"mem-applier-reply-d",
		"mem-applier-reply-e",
		"mem-applier-reply-f",
	}
	for _, memberID := range memberIDs[1:] {
		userID := "user-" + memberID
		insertMessagingUser(t, ctx, db, userID, userID+"@example.com", now)
		insertMessagingHumanMember(t, ctx, db, memberID, "workspace-applier", userID, now)
	}

	for index, memberID := range memberIDs {
		plan := scopeApplierPlanForTest(now.Add(time.Duration(index+1) * time.Minute))
		plan.MessageWrite.ID = fmt.Sprintf("msg-applier-recent-author-%d", index)
		plan.MessageWrite.ClientMessageID = ""
		plan.MessageWrite.IdempotencyPayloadDigest = ""
		plan.MessageWrite.Seq = int64(index + 2)
		plan.MessageWrite.WorkspaceGlobalSeq = int64(index + 102)
		plan.MessageWrite.AuthorMemberID = memberID
		plan.MessageWrite.NotifyPrincipalMemberID = memberID
		plan.MessageWrite.IdempotencySubjectMemberID = memberID
		plan.SourceWrites = nil
		plan.MembershipMutations = nil
		plan.NotificationMutations = nil
		plan.OutboxEvents = nil
		plan.SideEffectProfile = MessagePlanSideEffectProfileNoOutbox
		_, err := applyMessagePlanTxForTest(t, ctx, db, plan)
		require.NoError(t, err)
	}

	require.Equal(t, []string{
		"mem-applier-reply-f",
		"mem-applier-reply-e",
		"mem-applier-reply-d",
		"mem-applier-reply-c",
		"mem-applier-reply-b",
	}, loadScopeRecentAuthorMemberIDsForTest(t, ctx, db, "scope-applier-thread"))

	plan := scopeApplierPlanForTest(now.Add(10 * time.Minute))
	plan.MessageWrite.ID = "msg-applier-recent-author-repeat"
	plan.MessageWrite.ClientMessageID = ""
	plan.MessageWrite.IdempotencyPayloadDigest = ""
	plan.MessageWrite.Seq = 8
	plan.MessageWrite.WorkspaceGlobalSeq = 108
	plan.MessageWrite.AuthorMemberID = "mem-applier-reply-c"
	plan.MessageWrite.NotifyPrincipalMemberID = "mem-applier-reply-c"
	plan.MessageWrite.IdempotencySubjectMemberID = "mem-applier-reply-c"
	plan.SourceWrites = nil
	plan.MembershipMutations = nil
	plan.NotificationMutations = nil
	plan.OutboxEvents = nil
	plan.SideEffectProfile = MessagePlanSideEffectProfileNoOutbox
	_, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.NoError(t, err)
	require.Equal(t, []string{
		"mem-applier-reply-c",
		"mem-applier-reply-f",
		"mem-applier-reply-e",
		"mem-applier-reply-d",
		"mem-applier-reply-b",
	}, loadScopeRecentAuthorMemberIDsForTest(t, ctx, db, "scope-applier-thread"))

	excluded := scopeApplierPlanForTest(now.Add(11 * time.Minute))
	excluded.MessageWrite.ID = "msg-applier-recent-author-excluded"
	excluded.MessageWrite.ClientMessageID = ""
	excluded.MessageWrite.IdempotencyPayloadDigest = ""
	excluded.MessageWrite.Seq = 9
	excluded.MessageWrite.WorkspaceGlobalSeq = 109
	excluded.MessageWrite.AuthorMemberID = "mem-applier-owner"
	excluded.MessageWrite.MessageType = MessageTypeSystem
	excluded.SourceWrites = nil
	excluded.MembershipMutations = nil
	excluded.NotificationMutations = nil
	excluded.OutboxEvents = nil
	excluded.SideEffectProfile = MessagePlanSideEffectProfileNoOutbox
	_, err = applyMessagePlanTxForTest(t, ctx, db, excluded)
	require.NoError(t, err)
	require.Equal(t, []string{
		"mem-applier-reply-c",
		"mem-applier-reply-f",
		"mem-applier-reply-e",
		"mem-applier-reply-d",
		"mem-applier-reply-b",
	}, loadScopeRecentAuthorMemberIDsForTest(t, ctx, db, "scope-applier-thread"))
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	workflowMetadata, _, err := EncodeMessageMetadata(BuildWorkflowContextMetadata(WorkflowContextMetadataParams{
		WorkspaceID: "workspace-applier",
		RunID:       "wri-applier",
		EffectID:    "wfx-applier",
	}))
	require.NoError(t, err)
	workflow := scopeApplierPlanForTest(now)
	workflow.MessageWrite.Metadata = workflowMetadata
	workflow.SourceWrites = nil
	workflow.MembershipMutations = nil
	workflow.NotificationMutations = nil
	workflow.OutboxEvents = nil
	workflow.SideEffectProfile = MessagePlanSideEffectProfileNoOutbox

	_, err = applyMessagePlanTxForTest(t, ctx, db, workflow)
	require.NoError(t, err)
	require.Empty(t, loadScopeRecentAuthorMemberIDsForTest(t, ctx, db, "scope-applier-thread"))

	actualReply := scopeApplierPlanForTest(now.Add(time.Minute))
	actualReply.MessageWrite.ID = "msg-applier-actual-reply"
	actualReply.MessageWrite.ClientMessageID = "client-applier-actual-reply"
	actualReply.MessageWrite.Seq = 3
	actualReply.MessageWrite.WorkspaceGlobalSeq = 103
	actualReply.SourceWrites = nil
	actualReply.MembershipMutations = nil
	actualReply.NotificationMutations = nil
	actualReply.OutboxEvents = nil
	actualReply.SideEffectProfile = MessagePlanSideEffectProfileNoOutbox
	_, err = applyMessagePlanTxForTest(t, ctx, db, actualReply)
	require.NoError(t, err)
	require.Equal(t, []string{"mem-applier-owner"}, loadScopeRecentAuthorMemberIDsForTest(t, ctx, db, "scope-applier-thread"))
}

func loadScopeRecentAuthorMemberIDsForTest(t *testing.T, ctx context.Context, db coredb.QueryRower, scopeID string) []string {
	t.Helper()
	var raw []byte
	require.NoError(t, db.QueryRow(ctx, `
SELECT COALESCE(recent_author_member_ids, JSON_ARRAY())
FROM scopes
WHERE id = $1
`, scopeID).Scan(&raw))
	var memberIDs []string
	require.NoError(t, json.Unmarshal(raw, &memberIDs))
	return memberIDs
}

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

	_, err := ApplyMessagePlanTx(context.Background(), nil, MessagePlan{
		SidecarWrites: MessageSidecarWrites{
			AttachmentClaims: []AttachmentClaimWrite{{
				WorkspaceID: "wsp-sidecar",
				ScopeID:     "scp-sidecar",
				MessageID:   "msg-sidecar",
			}},
		},
	})
	require.ErrorContains(t, err, "message plan sidecar writes require ApplyMessagePlanTxWithSidecarWriter")
}

func loadScopeVisibleSummaryForTest(t *testing.T, ctx context.Context, db coredb.QueryRower, scopeID string) (int64, *time.Time) {
	t.Helper()

	var count int64
	var last sql.NullTime
	require.NoError(t, db.QueryRow(ctx, `
SELECT visible_message_count, visible_last_message_at
FROM scopes
WHERE id = $1
`, scopeID).Scan(&count, &last))
	return count, nullableTimePtr(last)
}

func applyMessagePlanTxForTest(t *testing.T, ctx context.Context, db coredb.DB, plan MessagePlan) (MessageApplyResult, error) {
	t.Helper()

	var result MessageApplyResult
	err := coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		applied, err := ApplyMessagePlanTx(ctx, tx, plan)
		result = applied
		return err
	})
	if err != nil {
		return MessageApplyResult{}, err
	}
	return result, nil
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	plan := scopeApplierPlanForTest(now)
	plan.MessageWrite.AuthorMemberID = ""
	_, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.Error(t, err)

	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_messages", "id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_memberships", "scope_id = $1 AND member_id = $2", "scope-applier-thread", "mem-applier-private"))
	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_fanout_outbox", "message_id = $1", plan.MessageWrite.ID))
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	plan := scopeApplierPlanForTest(now)
	plan.OutboxEvents[0].DedupeKey = ""
	_, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.Error(t, err)

	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_messages", "id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_message_sources", "message_id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_message_search_docs", "message_id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_memberships", "scope_id = $1 AND member_id = $2", "scope-applier-thread", "mem-applier-private"))
	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_notification_settings", "scope_id = $1 AND member_id = $2", "scope-applier-thread", "mem-applier-private"))
	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_fanout_outbox", "message_id = $1", plan.MessageWrite.ID))

	var latestSeq, latestGlobalSeq int64
	require.NoError(t, db.QueryRow(ctx, `
SELECT latest_seq, latest_global_seq
FROM scopes
WHERE id = $1
`, "scope-applier-thread").Scan(&latestSeq, &latestGlobalSeq))
	require.Equal(t, int64(1), latestSeq)
	require.Equal(t, int64(101), latestGlobalSeq)
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	plan := scopeApplierPlanForTest(now)
	plan.TaskContextIngressRefs = []ScopeTaskContextIngressRef{{
		ScopeID:     "scope-applier-thread",
		TaskID:      "task-applier",
		ExecutionID: "exec-applier",
		RecipientID: "mem-applier-task-agent",
		CreatedAt:   now,
	}}
	_, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.ErrorContains(t, err, "task-native ingress writer is not connected")

	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_messages", "id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_fanout_outbox", "message_id = $1", plan.MessageWrite.ID))
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	plan := scopeApplierPlanForTest(now)
	plan.OutboxEvents = nil
	_, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.ErrorContains(t, err, "requires a \"scope_fanout\" outbox event")

	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_messages", "id = $1", plan.MessageWrite.ID))
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	plan := scopeApplierPlanForTest(now)
	plan.OutboxEvents[0].EventRefType = DeliverySourceRefActivityEvent
	plan.OutboxEvents[0].EventRefID = "act-not-message"
	plan.OutboxEvents[0].MessageID = ""
	_, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.ErrorContains(t, err, "delivery intent message source builder")

	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_messages", "id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_fanout_outbox", "event_ref_id = $1", "act-not-message"))
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	plan := scopeApplierPlanForTest(now)
	plan.SideEffectProfile = MessagePlanSideEffectProfileNoOutbox
	_, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.ErrorContains(t, err, "must not include outbox events")

	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_messages", "id = $1", plan.MessageWrite.ID))
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	plan := scopeApplierPlanForTest(now)
	plan.SideEffectProfile = MessagePlanSideEffectProfile("direct_realtime")
	_, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.ErrorContains(t, err, "unknown message plan side-effect profile")

	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_messages", "id = $1", plan.MessageWrite.ID))
}

func scopeApplierPlanForTest(now time.Time) MessagePlan {
	return MessagePlan{
		MessageWrite: ScopeMessageRecord{
			ID:                         "msg-applier",
			WorkspaceID:                "workspace-applier",
			ScopeID:                    "scope-applier-thread",
			Seq:                        2,
			WorkspaceGlobalSeq:         102,
			AuthorMemberID:             "mem-applier-owner",
			AuthorType:                 ScopeActorHuman,
			NotifyPrincipalMemberID:    "mem-applier-owner",
			IdempotencySubjectMemberID: "mem-applier-owner",
			ClientMessageID:            "client-applier",
			IdempotencyPayloadDigest:   strings.Repeat("a", 64),
			MessageType:                "text",
			Content:                    "@private hello",
			CreatedAt:                  now,
			UpdatedAt:                  now,
		},
		SideEffectProfile: MessagePlanSideEffectProfileScopeFanout,
		SourceWrites: []ScopeMessageSourceRecord{{
			TargetScopeID:   "scope-applier-thread",
			MessageID:       "msg-applier",
			Ordinal:         0,
			SourceScopeID:   "scope-applier-channel",
			SourceSeq:       1,
			SourceMessageID: "msg-applier-root",
			SourceKind:      "root",
			CreatedAt:       now,
		}},
		MembershipMutations: []ScopeMembershipRecord{{
			ScopeID:       "scope-applier-thread",
			MemberID:      "mem-applier-private",
			Visibility:    "private",
			OwnerMemberID: "mem-applier-owner",
			CreatedAt:     now,
			UpdatedAt:     now,
		}},
		NotificationMutations: []ScopeNotificationSettingRecord{{
			ScopeID:   "scope-applier-thread",
			MemberID:  "mem-applier-private",
			State:     "active",
			CreatedAt: now,
			UpdatedAt: now,
		}},
		OutboxEvents: []ScopeFanoutOutboxRecord{{
			ID:            "out-applier-fanout",
			WorkspaceID:   "workspace-applier",
			ScopeID:       "scope-applier-thread",
			MessageID:     "msg-applier",
			EventRefType:  "message",
			EventRefID:    "msg-applier",
			FanoutType:    ScopeOutboxFanoutScope,
			DedupeKey:     "scope-applier-thread:msg-applier:fanout",
			Status:        "pending",
			NextAttemptAt: now,
			CreatedAt:     now,
		}},
	}
}

func seedScopeApplierBaseData(t *testing.T, ctx context.Context, db coredb.QueryExecer, now time.Time) {
	t.Helper()

	insertMessagingWorkspace(t, ctx, db, "workspace-applier", now)
	insertMessagingUser(t, ctx, db, "user-applier-owner", "scope-applier-owner@example.com", now)
	insertMessagingHumanMember(t, ctx, db, "mem-applier-owner", "workspace-applier", "user-applier-owner", now)
	insertMessagingAgentMember(t, ctx, db, "mem-applier-private", "workspace-applier", now)
	insertMessagingAgentMember(t, ctx, db, "mem-applier-task-agent", "workspace-applier", now)

	repos := NewScopeRepositories(db)
	require.NoError(t, repos.Scopes.UpsertScope(ctx, ScopeRecord{
		ID:                "scope-applier-channel",
		WorkspaceID:       "workspace-applier",
		Type:              "channel",
		IdentityKey:       "channel:scope-applier",
		LatestSeq:         1,
		LatestGlobalSeq:   101,
		LatestMessageAt:   &now,
		Status:            "active",
		CreatedByMemberID: "mem-applier-owner",
		CreatedAt:         now,
		UpdatedAt:         now,
	}))
	require.NoError(t, repos.Scopes.UpsertScope(ctx, ScopeRecord{
		ID:                 "scope-applier-thread",
		WorkspaceID:        "workspace-applier",
		Type:               "thread",
		ParentScopeID:      "scope-applier-channel",
		RootMessageID:      "msg-applier-root",
		RootMessageShortID: "root",
		IdentityKey:        "thread:scope-applier",
		LatestSeq:          1,
		LatestGlobalSeq:    101,
		LatestMessageAt:    &now,
		Status:             "active",
		CreatedByMemberID:  "mem-applier-owner",
		CreatedAt:          now,
		UpdatedAt:          now,
	}))
	require.NoError(t, repos.Messages.Insert(ctx, ScopeMessageRecord{
		ID:                 "msg-applier-root",
		WorkspaceID:        "workspace-applier",
		ScopeID:            "scope-applier-channel",
		Seq:                1,
		WorkspaceGlobalSeq: 101,
		AuthorMemberID:     "mem-applier-owner",
		AuthorType:         ScopeActorHuman,
		MessageType:        "text",
		Content:            "root",
		CreatedAt:          now,
		UpdatedAt:          now,
	}))
}

func scopeApplierNowForTest() time.Time {
	return time.Date(2026, 5, 13, 4, 0, 0, 0, time.UTC)
}

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

	ctx := context.Background()
	db := startMessagingTestPool(t, ctx)
	now := scopeApplierNowForTest()
	seedScopeApplierBaseData(t, ctx, db, now)

	plan := scopeApplierPlanForTest(now)
	plan.MessageWrite.ID = "msg-applier-no-outbox"
	plan.MessageWrite.ClientMessageID = "client-applier-no-outbox"
	plan.MessageWrite.Seq = 3
	plan.MessageWrite.WorkspaceGlobalSeq = 103
	plan.SourceWrites = nil
	plan.MembershipMutations = nil
	plan.NotificationMutations = nil
	plan.OutboxEvents = nil
	plan.SideEffectProfile = MessagePlanSideEffectProfileNoOutbox

	result, err := applyMessagePlanTxForTest(t, ctx, db, plan)
	require.NoError(t, err)
	require.True(t, result.Message.Inserted)

	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_messages", "id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_message_search_docs", "message_id = $1", plan.MessageWrite.ID))
	require.Equal(t, int64(1), countScopeRows(t, ctx, db, "scope_sync_dirty", "workspace_id = $1 AND scope_id = $2 AND latest_message_seq = $3", plan.MessageWrite.WorkspaceID, plan.MessageWrite.ScopeID, plan.MessageWrite.Seq))
	require.Equal(t, int64(0), countScopeRows(t, ctx, db, "scope_fanout_outbox", "message_id = $1", plan.MessageWrite.ID))

	var latestSeq, latestGlobalSeq int64
	require.NoError(t, db.QueryRow(ctx, `
SELECT latest_seq, latest_global_seq
FROM scopes
WHERE id = $1
`, "scope-applier-thread").Scan(&latestSeq, &latestGlobalSeq))
	require.Equal(t, plan.MessageWrite.Seq, latestSeq)
	require.Equal(t, plan.MessageWrite.WorkspaceGlobalSeq, latestGlobalSeq)
}
