package message

import (
	"encoding/base64"
	"strings"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestTaskWatcherCursorRoundTrip(t *testing.T) {
	t.Parallel()
	want := taskWatcherCursor{
		Version:     taskWatcherCursorVersion,
		DisplayName: "alpha agent",
		ActorType:   AuthorActorTypeAgent,
		MemberID:    "mem_alpha",
	}
	encoded := encodeTaskWatcherCursor(want)
	require.NotEmpty(t, encoded)

	got, err := decodeTaskWatcherCursor(encoded)
	require.NoError(t, err)
	require.Equal(t, want, got)
}

func TestTaskWatcherCursorRoundTripSupportsUtf8mb4VarcharBounds(t *testing.T) {
	t.Parallel()
	want := taskWatcherCursor{
		Version:     taskWatcherCursorVersion,
		DisplayName: strings.Repeat("🙂", 255),
		ActorType:   AuthorActorTypeHuman,
		MemberID:    strings.Repeat("界", 255),
	}
	require.Equal(t, maxTaskWatcherSortBytes, len(want.DisplayName))
	encoded := encodeTaskWatcherCursor(want)
	require.LessOrEqual(t, len(encoded), maxTaskWatcherCursorBytes)

	got, err := decodeTaskWatcherCursor(encoded)
	require.NoError(t, err, "a server-generated cursor must always be accepted")
	require.Equal(t, want, got)
}

func TestTaskWatcherCursorRejectsMalformedAndOversize(t *testing.T) {
	t.Parallel()
	wellFormedCursor := base64.RawURLEncoding.EncodeToString([]byte("d=a&m=m&t=agent&v=1"))
	require.Equal(t, "Q", wellFormedCursor[len(wellFormedCursor)-1:])
	malformedTrailingBits := wellFormedCursor[:len(wellFormedCursor)-1] + "R"
	cursorWithEmbeddedNewline := wellFormedCursor[:4] + "\r\n" + wellFormedCursor[4:]

	for _, raw := range []string{
		"not-base64",
		malformedTrailingBits,
		cursorWithEmbeddedNewline,
		base64.RawURLEncoding.EncodeToString([]byte("d=alpha&m=mem_alpha&t=agent&v=1&x=unexpected")),
		base64.RawURLEncoding.EncodeToString([]byte("d=alpha&m=mem_alpha&m=mem_other&t=agent&v=1")),
		base64.RawURLEncoding.EncodeToString([]byte("d=alpha&t=agent&v=1")),
		encodeTaskWatcherCursor(taskWatcherCursor{
			Version:     taskWatcherCursorVersion + 1,
			DisplayName: "alpha",
			ActorType:   AuthorActorTypeAgent,
			MemberID:    "mem_alpha",
		}),
		encodeTaskWatcherCursor(taskWatcherCursor{
			Version:     taskWatcherCursorVersion,
			DisplayName: "alpha",
			ActorType:   AuthorActorTypeService,
			MemberID:    "mem_alpha",
		}),
		encodeTaskWatcherCursor(taskWatcherCursor{
			Version:     taskWatcherCursorVersion,
			DisplayName: strings.Repeat("a", maxTaskWatcherSortBytes+1),
			ActorType:   AuthorActorTypeAgent,
			MemberID:    "mem_alpha",
		}),
	} {
		_, err := decodeTaskWatcherCursor(raw)
		require.Error(t, err)
	}
}

func TestNormalizeTaskWatcherPageSizeIsBounded(t *testing.T) {
	t.Parallel()
	require.Equal(t, DefaultTaskWatcherPageSize, normalizeTaskWatcherPageSize(0))
	require.Equal(t, DefaultTaskWatcherPageSize, normalizeTaskWatcherPageSize(-1))
	require.Equal(t, 1, normalizeTaskWatcherPageSize(1))
	require.Equal(t, MaxTaskWatcherPageSize, normalizeTaskWatcherPageSize(MaxTaskWatcherPageSize+1))
}

func TestVisibleTaskWatcherQueryUsesOnlyAuthoritativeStateAndSafeRosterProjection(t *testing.T) {
	t.Parallel()
	require.Contains(t, visibleTaskWatcherProfilesSQL, "task_watch_state = 'active'")
	require.Contains(t, visibleTaskWatcherProfilesSQL, "task_watch_state = 'auto-wake'")
	require.Contains(t, visibleTaskWatcherProfilesSQL, "sm.removed_at IS NULL")
	require.Contains(t, visibleTaskWatcherProfilesSQL, "sm.archived_at IS NULL")
	require.Contains(t, visibleTaskWatcherProfilesSQL, "sm.effective_to_seq IS NULL")
	require.Contains(t, visibleTaskWatcherProfilesSQL, "m.status = 'active'")
	require.Contains(t, visibleTaskWatcherProfilesSQL, "a.status <> 'archived'")
	require.Contains(t, visibleTaskWatcherProfilesSQL, "mac.status = 'active'")
	require.Contains(t, visibleTaskWatcherProfilesSQL, "LIMIT $6")
	require.NotContains(t, visibleTaskWatcherProfilesSQL, "sm.visibility")
	require.NotContains(t, visibleTaskWatcherProfilesSQL, "owner_member_id")
	require.NotContains(t, visibleTaskWatcherProfilesSQL, "runtime")
	require.NotContains(t, visibleTaskWatcherProfilesSQL, "assignee")
	require.NotContains(t, visibleTaskWatcherProfilesSQL, "created_by_member_id")
	require.NotContains(t, visibleTaskWatcherProfilesSQL, "mention")
}
