package textutil

import (
	"strings"
	"testing"
	"unicode/utf8"

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

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

	require.Equal(t, "abc", Truncate("abc", 5))
	require.Equal(t, "abc", Truncate("abc", 3))
	require.Equal(t, "ab", Truncate("abc", 2))
	require.Equal(t, "", Truncate("abc", 0))
	require.Equal(t, "", Truncate("abc", -1))
	require.Equal(t, "", Truncate("", 10))
}

// The case the byte-slicing version got wrong. Every cut of a Chinese string
// must still be storable, whichever byte the limit lands on.
func TestTruncateNeverSplitsARune(t *testing.T) {
	t.Parallel()

	s := "集群写入延迟升高，疑似 TiKV 写盘变慢"
	for max := 0; max <= len(s)+2; max++ {
		got := Truncate(s, max)
		require.True(t, utf8.ValidString(got), "invalid UTF-8 at max=%d", max)
		require.LessOrEqual(t, len(got), max)
		require.True(t, strings.HasPrefix(s, got), "not a prefix at max=%d", max)
	}
}

// A cut inside a multi-byte rune drops that whole rune rather than half of it,
// which costs at most the three bytes asserted here.
func TestTruncateDropsAtMostOneRune(t *testing.T) {
	t.Parallel()

	s := "日本語" // three runes, three bytes each
	require.Equal(t, "日本語", Truncate(s, 9))
	require.Equal(t, "日本", Truncate(s, 8))
	require.Equal(t, "日本", Truncate(s, 7))
	require.Equal(t, "日本", Truncate(s, 6))
	require.Equal(t, "日", Truncate(s, 5))
	require.Equal(t, "", Truncate(s, 2))

	// Four-byte runes (emoji, rarer CJK) walk back the furthest.
	require.Equal(t, "", Truncate("🚨", 3))
	require.Equal(t, "🚨", Truncate("🚨", 4))
}
