package rewind

import (
	"context"
	"database/sql"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/access"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
	"github.com/tidbcloud/AgentTeam/internal/store/testfixture"
)

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

	ctx := context.Background()
	db := startRewindRollbackDB(t, ctx)
	now := time.Date(2026, 6, 29, 3, 15, 0, 0, time.UTC)

	const (
		workspaceID   = "wsp_rewind_tail_reply_thread"
		userID        = "usr_rewind_tail_reply_thread"
		memberID      = "mem_rewind_tail_reply_thread"
		parentScopeID = "chn_rewind_tail_reply_thread"
		childScopeID  = "thr_rewind_tail_reply_thread"
		keepMessageID = "msg_rewind_tail_reply_keep"
		rootMessageID = "msg_rewind_tail_reply_root"
		childOneID    = "msg_rewind_tail_reply_child_one"
		childTwoID    = "msg_rewind_tail_reply_child_two"
	)

	seedRewindReplyThreadWorkspace(t, ctx, db, workspaceID, userID, memberID, now)
	seedRewindChannelScope(t, db, workspaceID, parentScopeID, 2, 2, now)
	seedRewindReplyThreadScope(t, db, workspaceID, parentScopeID, childScopeID, rootMessageID, 2, 4, now.Add(2*time.Second))
	seedRewindScopeMessage(t, db, workspaceID, parentScopeID, keepMessageID, 1, 1, memberID, "keep", now)
	seedRewindScopeMessage(t, db, workspaceID, parentScopeID, rootMessageID, 2, 2, memberID, "root with reply thread", now.Add(time.Second))
	seedRewindScopeMessage(t, db, workspaceID, childScopeID, childOneID, 1, 3, memberID, "child one", now.Add(2*time.Second))
	seedRewindScopeMessage(t, db, workspaceID, childScopeID, childTwoID, 2, 4, memberID, "child two", now.Add(3*time.Second))

	store := New(db, rewindPlanningTestOptions(workspaceID, userID, memberID, parentScopeID))
	plan, err := store.PrepareRewindForUser(ctx, userID, workspaceID, rootMessageID)
	require.NoError(t, err)
	require.Equal(t, "channel", plan.Scope)
	require.Equal(t, []string{rootMessageID}, plan.SuffixMessageIDs)
	require.Len(t, plan.CascadeReplyThreads, 1)
	require.Equal(t, childScopeID, plan.CascadeReplyThreads[0].CascadeScopeID())
	require.Equal(t, rootMessageID, plan.CascadeReplyThreads[0].ParentMessageID)
	require.Equal(t, []string{childOneID, childTwoID}, plan.CascadeReplyThreads[0].MessageIDs)
	require.NoError(t, plan.ValidateSuffixBoundaries())

	operationID, err := store.BeginRewindOperation(ctx, plan)
	require.NoError(t, err)
	require.NoError(t, store.ApplyRewind(ctx, operationID, plan))

	require.False(t, rewindMessageIsRewound(t, ctx, db, keepMessageID))
	require.True(t, rewindMessageIsRewound(t, ctx, db, rootMessageID))
	require.True(t, rewindMessageIsRewound(t, ctx, db, childOneID))
	require.True(t, rewindMessageIsRewound(t, ctx, db, childTwoID))

	var childRows int64
	require.NoError(t, db.QueryRow(ctx, `
SELECT COUNT(*)
FROM scope_messages
WHERE workspace_id = $1
  AND scope_id = $2
`, workspaceID, childScopeID).Scan(&childRows))
	require.Equal(t, int64(2), childRows)

	var archivedAt sql.NullTime
	require.NoError(t, db.QueryRow(ctx, `
SELECT archived_at
FROM scopes
WHERE id = $1
`, childScopeID).Scan(&archivedAt))
	require.True(t, archivedAt.Valid)
}

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

	ctx := context.Background()
	db := startRewindRollbackDB(t, ctx)
	now := time.Date(2026, 6, 29, 3, 18, 0, 0, time.UTC)

	const (
		workspaceID     = "wsp_rewind_tail_reply_notice"
		userID          = "usr_rewind_tail_reply_notice"
		memberID        = "mem_rewind_tail_reply_notice"
		parentScopeID   = "chn_rewind_tail_reply_notice"
		childScopeID    = "thr_rewind_tail_reply_notice"
		keepMessageID   = "msg_rewind_tail_reply_notice_keep"
		rootMessageID   = "msg_rewind_tail_reply_notice_root"
		childMessageID  = "msg_rewind_tail_reply_notice_child"
		noticeMessageID = "msg_rewind_tail_reply_notice_system"
	)

	seedRewindReplyThreadWorkspace(t, ctx, db, workspaceID, userID, memberID, now)
	seedRewindChannelScope(t, db, workspaceID, parentScopeID, 3, 4, now)
	seedRewindReplyThreadScope(t, db, workspaceID, parentScopeID, childScopeID, rootMessageID, 1, 3, now.Add(2*time.Second))
	seedRewindScopeMessage(t, db, workspaceID, parentScopeID, keepMessageID, 1, 1, memberID, "keep", now)
	seedRewindScopeMessage(t, db, workspaceID, parentScopeID, rootMessageID, 2, 2, memberID, "root with reply thread", now.Add(time.Second))
	seedRewindScopeMessage(t, db, workspaceID, childScopeID, childMessageID, 1, 3, memberID, "child reply", now.Add(2*time.Second))
	seedRewindNoticeScopeMessage(t, db, workspaceID, parentScopeID, noticeMessageID, 3, 4, memberID, "system.rewind.conversation", now.Add(3*time.Second))

	store := New(db, rewindPlanningTestOptions(workspaceID, userID, memberID, parentScopeID))
	plan, err := store.PrepareRewindForUser(ctx, userID, workspaceID, rootMessageID)
	require.NoError(t, err)
	require.Equal(t, []string{rootMessageID}, plan.SuffixMessageIDs)
	require.Len(t, plan.CascadeReplyThreads, 1)
	require.Equal(t, childScopeID, plan.CascadeReplyThreads[0].CascadeScopeID())
	require.Equal(t, rootMessageID, plan.CascadeReplyThreads[0].ParentMessageID)

	operationID, err := store.BeginRewindOperation(ctx, plan)
	require.NoError(t, err)
	require.NoError(t, store.ApplyRewind(ctx, operationID, plan))

	require.False(t, rewindMessageIsRewound(t, ctx, db, keepMessageID))
	require.True(t, rewindMessageIsRewound(t, ctx, db, rootMessageID))
	require.False(t, rewindMessageIsRewound(t, ctx, db, noticeMessageID))
	require.True(t, rewindMessageIsRewound(t, ctx, db, childMessageID))

	var archivedAt sql.NullTime
	require.NoError(t, db.QueryRow(ctx, `
SELECT archived_at
FROM scopes
WHERE id = $1
`, childScopeID).Scan(&archivedAt))
	require.True(t, archivedAt.Valid)
}

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

	ctx := context.Background()
	db := startRewindRollbackDB(t, ctx)
	now := time.Date(2026, 6, 29, 3, 19, 0, 0, time.UTC)

	const (
		workspaceID     = "wsp_rewind_keep_prior_notice"
		userID          = "usr_rewind_keep_prior_notice"
		memberID        = "mem_rewind_keep_prior_notice"
		parentScopeID   = "chn_rewind_keep_prior_notice"
		keepMessageID   = "msg_rewind_keep_prior_notice_keep"
		targetMessageID = "msg_rewind_keep_prior_notice_target"
		laterMessageID  = "msg_rewind_keep_prior_notice_later"
		noticeMessageID = "msg_rewind_keep_prior_notice_system"
	)

	seedRewindReplyThreadWorkspace(t, ctx, db, workspaceID, userID, memberID, now)
	seedRewindChannelScope(t, db, workspaceID, parentScopeID, 4, 4, now)
	seedRewindScopeMessage(t, db, workspaceID, parentScopeID, keepMessageID, 1, 1, memberID, "a", now)
	seedRewindScopeMessage(t, db, workspaceID, parentScopeID, targetMessageID, 2, 2, memberID, "b", now.Add(time.Second))
	seedRewindScopeMessage(t, db, workspaceID, parentScopeID, laterMessageID, 3, 3, memberID, "c", now.Add(2*time.Second))
	seedRewindNoticeScopeMessage(t, db, workspaceID, parentScopeID, noticeMessageID, 4, 4, memberID, "system.rewind.conversation", now.Add(3*time.Second))

	store := New(db, rewindPlanningTestOptions(workspaceID, userID, memberID, parentScopeID))
	plan, err := store.PrepareRewindForUser(ctx, userID, workspaceID, targetMessageID)
	require.NoError(t, err)
	require.Equal(t, []string{targetMessageID, laterMessageID}, plan.SuffixMessageIDs)
	require.Empty(t, plan.CascadeReplyThreads)
	require.NoError(t, plan.ValidateSuffixBoundaries())

	operationID, err := store.BeginRewindOperation(ctx, plan)
	require.NoError(t, err)
	require.NoError(t, store.ApplyRewind(ctx, operationID, plan))

	require.False(t, rewindMessageIsRewound(t, ctx, db, keepMessageID))
	require.True(t, rewindMessageIsRewound(t, ctx, db, targetMessageID))
	require.True(t, rewindMessageIsRewound(t, ctx, db, laterMessageID))
	require.False(t, rewindMessageIsRewound(t, ctx, db, noticeMessageID))

	currentIDs, err := LoadCurrentSuffixMessageIDs(ctx, db, workspaceID, parentScopeID, targetMessageID)
	require.NoError(t, err)
	require.Empty(t, currentIDs)
}

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

	ctx := context.Background()
	db := startRewindRollbackDB(t, ctx)
	now := time.Date(2026, 6, 29, 3, 20, 0, 0, time.UTC)

	const (
		workspaceID    = "wsp_rewind_nontail_reply_thread"
		userID         = "usr_rewind_nontail_reply_thread"
		memberID       = "mem_rewind_nontail_reply_thread"
		parentScopeID  = "chn_rewind_nontail_reply_thread"
		childScopeID   = "thr_rewind_nontail_reply_thread"
		targetMessage  = "msg_rewind_nontail_reply_target"
		laterMessageID = "msg_rewind_nontail_reply_later"
		childMessageID = "msg_rewind_nontail_reply_child"
	)

	seedRewindReplyThreadWorkspace(t, ctx, db, workspaceID, userID, memberID, now)
	seedRewindChannelScope(t, db, workspaceID, parentScopeID, 2, 2, now)
	seedRewindReplyThreadScope(t, db, workspaceID, parentScopeID, childScopeID, targetMessage, 1, 3, now.Add(2*time.Second))
	seedRewindScopeMessage(t, db, workspaceID, parentScopeID, targetMessage, 1, 1, memberID, "target with reply thread", now)
	seedRewindScopeMessage(t, db, workspaceID, parentScopeID, laterMessageID, 2, 2, memberID, "later message keeps fork non-tail", now.Add(time.Second))
	seedRewindScopeMessage(t, db, workspaceID, childScopeID, childMessageID, 1, 3, memberID, "child reply", now.Add(2*time.Second))

	store := New(db, rewindPlanningTestOptions(workspaceID, userID, memberID, parentScopeID))
	_, err := store.PrepareRewindForUser(ctx, userID, workspaceID, targetMessage)

	require.ErrorContains(t, err, "message in the suffix has a reply thread")
}

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

	ctx := context.Background()
	db := startRewindRollbackDB(t, ctx)
	now := time.Date(2026, 6, 29, 3, 25, 0, 0, time.UTC)

	const (
		workspaceID       = "wsp_rewind_reply_thread_scope"
		userID            = "usr_rewind_reply_thread_scope"
		memberID          = "mem_rewind_reply_thread_scope"
		parentScopeID     = "chn_rewind_reply_thread_scope"
		childScopeID      = "thr_rewind_reply_thread_scope"
		parentMessageID   = "msg_rewind_reply_thread_parent"
		replyKeepID       = "msg_rewind_reply_thread_keep"
		replyTargetID     = "msg_rewind_reply_thread_target"
		replyLaterID      = "msg_rewind_reply_thread_later"
		replyThreadShort  = "rwthr"
		parentChannelName = "thread-scope"
	)

	seedRewindReplyThreadWorkspace(t, ctx, db, workspaceID, userID, memberID, now)
	seedRewindChannelScope(t, db, workspaceID, parentScopeID, 1, 1, now)
	seedRewindReplyThreadScope(t, db, workspaceID, parentScopeID, childScopeID, parentMessageID, 3, 4, now.Add(time.Second))
	seedRewindScopeMessage(t, db, workspaceID, parentScopeID, parentMessageID, 1, 1, memberID, "parent root", now)
	seedRewindScopeMessage(t, db, workspaceID, childScopeID, replyKeepID, 1, 2, memberID, "reply keep", now.Add(time.Second))
	seedRewindScopeMessage(t, db, workspaceID, childScopeID, replyTargetID, 2, 3, memberID, "reply target", now.Add(2*time.Second))
	seedRewindScopeMessage(t, db, workspaceID, childScopeID, replyLaterID, 3, 4, memberID, "reply later", now.Add(3*time.Second))

	store := New(db, rewindReplyThreadPlanningTestOptions(
		workspaceID,
		userID,
		memberID,
		parentScopeID,
		childScopeID,
		parentMessageID,
		replyThreadShort,
		parentChannelName,
	))
	plan, err := store.PrepareRewindForUser(ctx, userID, workspaceID, replyTargetID)
	require.NoError(t, err)
	require.Equal(t, "reply", plan.Scope)
	require.Equal(t, childScopeID, plan.RewindScopeID())
	require.Equal(t, parentScopeID, plan.RealtimeBroadcastScopeID())
	require.Equal(t, parentMessageID, plan.ReplyThreadParentMessageID())
	require.Equal(t, []string{replyTargetID, replyLaterID}, plan.SuffixMessageIDs)
	require.Empty(t, plan.CascadeReplyThreads)
	require.NoError(t, plan.ValidateSuffixBoundaries())

	operationID, err := store.BeginRewindOperation(ctx, plan)
	require.NoError(t, err)
	require.NoError(t, store.ApplyRewind(ctx, operationID, plan))

	require.False(t, rewindMessageIsRewound(t, ctx, db, parentMessageID))
	require.False(t, rewindMessageIsRewound(t, ctx, db, replyKeepID))
	require.True(t, rewindMessageIsRewound(t, ctx, db, replyTargetID))
	require.True(t, rewindMessageIsRewound(t, ctx, db, replyLaterID))

	var archivedAt sql.NullTime
	require.NoError(t, db.QueryRow(ctx, `
SELECT archived_at
FROM scopes
WHERE id = $1
`, childScopeID).Scan(&archivedAt))
	require.False(t, archivedAt.Valid)
}

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

	ctx := context.Background()
	db := startRewindRollbackDB(t, ctx)
	now := time.Date(2026, 7, 29, 10, 0, 0, 0, time.UTC)

	const (
		workspaceID        = "wsp_delete_reply_thread_projection"
		userID             = "usr_delete_reply_thread_projection"
		memberID           = "mem_delete_reply_thread_projection"
		parentScopeID      = "chn_delete_reply_thread_projection"
		deletedScopeID     = "thr_delete_reply_thread_projection"
		survivingScopeID   = "thr_keep_reply_thread_projection"
		deletedMessageID   = "msg_delete_reply_thread_projection"
		survivingMessageID = "msg_keep_reply_thread_projection"
	)

	seedRewindReplyThreadWorkspace(t, ctx, db, workspaceID, userID, memberID, now)
	seedRewindChannelScope(t, db, workspaceID, parentScopeID, 2, 2, now)
	seedRewindReplyThreadScope(t, db, workspaceID, parentScopeID, deletedScopeID, deletedMessageID, 0, 0, now)
	seedRewindReplyThreadScope(t, db, workspaceID, parentScopeID, survivingScopeID, survivingMessageID, 0, 0, now)
	for _, scopeID := range []string{deletedScopeID, survivingScopeID} {
		mustExecRecovery(t, db, `
INSERT INTO im_conversation_index (
  workspace_id, member_id, conversation_id, scope_id, kind, sort_at,
  last_event_seq, pinned_at, archived_at, projection_json, created_at, updated_at
)
VALUES ($1, $2, CONCAT('thread:', $3), $3, 'thread', $4,
        0, NULL, NULL, JSON_OBJECT('kind', 'replyThread'), $4, $4)
`, workspaceID, memberID, scopeID, now)
	}

	require.NoError(t, coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		return DeleteCascadeReplyThread(ctx, tx, workspaceID, CascadeReplyThread{ScopeID: deletedScopeID})
	}))

	var deletedProjectionCount int64
	require.NoError(t, db.QueryRow(ctx, `
SELECT COUNT(*) FROM im_conversation_index
WHERE workspace_id = $1 AND scope_id = $2
`, workspaceID, deletedScopeID).Scan(&deletedProjectionCount))
	require.Zero(t, deletedProjectionCount)

	var survivingProjectionCount int64
	require.NoError(t, db.QueryRow(ctx, `
SELECT COUNT(*) FROM im_conversation_index
WHERE workspace_id = $1 AND scope_id = $2
`, workspaceID, survivingScopeID).Scan(&survivingProjectionCount))
	require.Equal(t, int64(1), survivingProjectionCount)
}

func seedRewindReplyThreadWorkspace(t *testing.T, ctx context.Context, db coredb.QueryExecer, workspaceID string, userID string, memberID string, now time.Time) {
	t.Helper()

	mustExecRecovery(t, db, `
INSERT INTO users (id, email, name, display_name, avatar_url, is_admin, password_hash, email_verified_at, created_at, updated_at)
VALUES ($1, CONCAT($1, '@example.com'), $1, $1, '', TRUE, 'hash', $2, $2, $2)
`, userID, now)
	mustExecRecovery(t, db, `
INSERT INTO workspaces (id, organization_id, slug, name, latest_message_seq, created_at, updated_at)
VALUES ($1, $2, $1, $1, 4, $3, $3)
`, workspaceID, testfixture.SeedWorkspaceOrganization(t, ctx, db, workspaceID, now), now)
	mustExecRecovery(t, db, `
INSERT INTO members (id, workspace_id, type, display_name, user_id, workspace_role, created_at, updated_at)
VALUES ($1, $2, 'human', $1, $3, 'owner', $4, $4)
`, memberID, workspaceID, userID, now)
}

func seedRewindChannelScope(t *testing.T, db coredb.QueryExecer, workspaceID string, scopeID string, latestSeq int64, latestGlobalSeq int64, now time.Time) {
	t.Helper()

	mustExecRecovery(t, db, `
INSERT INTO scopes (
    id, workspace_id, type, identity_key, latest_seq, latest_global_seq,
    latest_message_at, archived_until_seq, status, created_at, updated_at
) VALUES (
    $1, $2, 'channel', CONCAT('channel:', $1), $3, $4,
    $5, 0, 'active', $5, $5
)
`, scopeID, workspaceID, latestSeq, latestGlobalSeq, now)
	mustExecRecovery(t, db, `
INSERT INTO scope_channel_profiles (
    scope_id, workspace_id, slug, display_name, description, visibility,
    owner_member_id, is_all_channel, created_at, updated_at
) VALUES ($1, $2, $1, $1, '', 'private', NULL, FALSE, $3, $3)
`, scopeID, workspaceID, now)
}

func seedRewindReplyThreadScope(t *testing.T, db coredb.QueryExecer, workspaceID string, parentScopeID string, scopeID string, parentMessageID string, latestSeq int64, latestGlobalSeq int64, now time.Time) {
	t.Helper()

	mustExecRecovery(t, db, `
INSERT INTO scopes (
    id, workspace_id, type, parent_scope_id, root_message_id, root_message_short_id,
    identity_key, latest_seq, latest_global_seq, latest_message_at,
    archived_until_seq, status, created_at, updated_at
) VALUES (
    $1, $2, 'thread', $3, $4, 'rwthrd',
    CONCAT('thread:', $3, ':', $4), $5, $6, $7,
    0, 'active', $7, $7
)
`, scopeID, workspaceID, parentScopeID, parentMessageID, latestSeq, latestGlobalSeq, now)
}

func seedRewindScopeMessage(t *testing.T, db coredb.QueryExecer, workspaceID string, scopeID string, messageID string, seq int64, globalSeq int64, memberID string, content string, createdAt time.Time) {
	t.Helper()

	mustExecRecovery(t, db, `
INSERT INTO scope_messages (
    id, workspace_id, scope_id, seq, workspace_global_seq,
    author_member_id, author_type, message_type, content, metadata, created_at, updated_at
) VALUES (
    $1, $2, $3, $4, $5,
    $6, 'human', 'text', $7, NULL, $8, $8
)
`, messageID, workspaceID, scopeID, seq, globalSeq, memberID, content, createdAt)
}

func seedRewindNoticeScopeMessage(t *testing.T, db coredb.QueryExecer, workspaceID string, scopeID string, messageID string, seq int64, globalSeq int64, memberID string, content string, createdAt time.Time) {
	t.Helper()

	metadata, _, err := message.EncodeMessageMetadata(message.BuildRewindSystemNoticeMetadata(memberID))
	require.NoError(t, err)

	mustExecRecovery(t, db, `
INSERT INTO scope_messages (
    id, workspace_id, scope_id, seq, workspace_global_seq,
    author_member_id, author_type, message_type, content, metadata, created_at, updated_at
) VALUES (
    $1, $2, $3, $4, $5,
    $6, 'human', $7, $8, $9, $10, $10
)
`, messageID, workspaceID, scopeID, seq, globalSeq, memberID, message.MessageTypeSystem, content, metadata, createdAt)
}

func rewindReplyThreadPlanningTestOptions(
	workspaceID string,
	userID string,
	humanMemberID string,
	parentScopeID string,
	replyThreadScopeID string,
	parentMessageID string,
	replyThreadShortID string,
	parentChannelName string,
) Options {
	return Options{
		LookupMessageChannelID: func(context.Context, string, string) (string, error) {
			return replyThreadScopeID, nil
		},
		ResolveAuthorizedHumanMessageTarget: func(context.Context, string, string, string) (access.HumanWorkspaceMember, message.ThreadAccess, error) {
			membership := access.HumanWorkspaceMember{
				Member: model.Member{
					ID:          humanMemberID,
					WorkspaceID: workspaceID,
					Type:        domain.MemberTypeHuman,
					UserID:      &userID,
				},
				WorkspaceRole: "owner",
			}
			target := message.ThreadAccess{
				ThreadID:   replyThreadScopeID,
				ThreadType: domain.ThreadTypeChannel,
				ChannelID:  replyThreadScopeID,
				ReplyThread: &message.ReplyThreadTarget{
					ParentMessageID:   parentMessageID,
					ShortID:           replyThreadShortID,
					ParentChannelID:   parentScopeID,
					ParentChannelName: parentChannelName,
				},
			}
			return membership, target, nil
		},
		RequireAgentOperation: func(context.Context, string, string, string) (access.HumanAgentAccess, error) {
			return access.HumanAgentAccess{}, nil
		},
		ReplaceReplyThreadParticipants: func(context.Context, coredb.Tx, string, []string) error {
			return nil
		},
		MergeReplyThreadParticipantIDs: message.MergeReplyThreadParticipantIDs,
	}
}

func rewindMessageIsRewound(t *testing.T, ctx context.Context, db coredb.Queryer, messageID string) bool {
	t.Helper()

	var content string
	var metadata []byte
	require.NoError(t, db.QueryRow(ctx, `
SELECT content, metadata
FROM scope_messages
WHERE id = $1
`, messageID).Scan(&content, &metadata))
	decoded, err := message.DecodeMessageMetadata(metadata)
	require.NoError(t, err)
	if message.MessageMetadataIsRewound(decoded) {
		require.Empty(t, content)
		return true
	}
	require.NotEmpty(t, content)
	return false
}
