package identifiers

import (
	"regexp"
	"strings"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
)

var messageIDPattern = regexp.MustCompile(`^[0-9a-f]{32}$`)

func TestValidWorkspaceID(t *testing.T) {
	t.Parallel()
	for _, test := range []struct {
		value string
		valid bool
	}{
		{value: "wsp_1", valid: true},
		{value: "workspace-A", valid: true},
		{value: "", valid: false},
		{value: ".", valid: false},
		{value: "wsp/1", valid: false},
		{value: "wsp%2F1", valid: false},
		{value: "工作区", valid: false},
		{value: string(make([]byte, 129)), valid: false},
	} {
		require.Equal(t, test.valid, ValidWorkspaceID(test.value), test.value)
	}
}

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

	memberID, err := NewMemberID()
	require.NoError(t, err)
	require.Len(t, memberID, memberIDLength)
	require.NoError(t, domain.ValidateMemberID(memberID))
}

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

	_, err := NewEntityID("")
	require.EqualError(t, err, "prefix is required")
}

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

	id, err := NewEntityID("ws")
	require.NoError(t, err)
	require.Equal(t, "ws_", id[:3])
	require.Len(t, id, len("ws_")+entityIDLength)
}

// TestNewEntityIDSuffixIsApproximatelyUniform guards against modulo bias.
// Without rejection sampling, alphabet slots 0..3 ('a','b','c','d') would
// each be picked with probability 8/256 while the remaining 32 slots get
// 7/256 — a ~14% relative bias. With rejection sampling, all 36 slots are
// picked with probability 7/252 ≈ 2.78%. We draw a large sample and assert
// the most over- and under-represented slots stay within ±20% of the mean.
func TestNewEntityIDSuffixIsApproximatelyUniform(t *testing.T) {
	t.Parallel()

	const samples = 200_000
	const alphabetSize = 36
	counts := make(map[byte]int, alphabetSize)

	for i := 0; i < samples; i++ {
		id, err := NewEntityID("ws")
		require.NoError(t, err)
		// id is "ws_" + 14 suffix chars; sample just the first suffix char
		// to keep the test cheap and still exercise the per-char sampler.
		counts[id[3]]++
	}

	mean := float64(samples) / float64(alphabetSize)
	lo := mean * 0.80
	hi := mean * 1.20

	for _, c := range []byte("abcdefghijklmnopqrstuvwxyz0123456789") {
		n := float64(counts[c])
		require.Greaterf(t, n, lo, "slot %q underrepresented: got %.0f, want >= %.0f", c, n, lo)
		require.Lessf(t, n, hi, "slot %q overrepresented: got %.0f, want <= %.0f", c, n, hi)
	}
}

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

	id, err := NewMessageID()
	require.NoError(t, err)
	require.Len(t, id, 32)
	require.True(t, messageIDPattern.MatchString(id))
}

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

	id, err := NewMessageID()
	require.NoError(t, err)
	require.True(t, messageIDPattern.MatchString(id))
}

func TestValidEntityIDSuffixExact14Base36(t *testing.T) {
	t.Parallel()
	valid, err := NewEntityID("usr")
	require.NoError(t, err)
	require.True(t, ValidEntityIDSuffix(valid[len("usr_"):]))
	require.False(t, ValidEntityIDSuffix(""), "empty suffix")
	require.False(t, ValidEntityIDSuffix("1"), "too short")
	require.False(t, ValidEntityIDSuffix("123456789012345"), "too long (15)")
	require.False(t, ValidEntityIDSuffix("1234567890123_"), "underscore in suffix")
	require.False(t, ValidEntityIDSuffix("ABCDEFGHIJKLMN"), "uppercase")
	require.False(t, ValidEntityIDSuffix("1234567890123!"), "punctuation")
	require.False(t, ValidEntityIDSuffix(" 1234567890123"), "leading space")
}

func TestValidMemberIDRawExact12Base36(t *testing.T) {
	t.Parallel()
	raw, err := NewMemberID()
	require.NoError(t, err)
	require.True(t, ValidMemberIDRaw(raw))
	require.False(t, ValidMemberIDRaw(""), "empty")
	require.False(t, ValidMemberIDRaw("1234567890"), "too short (10)")
	require.False(t, ValidMemberIDRaw("1234567890123"), "too long (13)")
	require.False(t, ValidMemberIDRaw("abc_12345678"), "underscore")
	require.False(t, ValidMemberIDRaw("ABC123456789"), "uppercase")
}

func TestValidMessageIDExact32Hex(t *testing.T) {
	t.Parallel()
	id, err := NewMessageID()
	require.NoError(t, err)
	require.True(t, ValidMessageID(id))
	require.False(t, ValidMessageID(""), "empty")
	require.False(t, ValidMessageID("abcdef"), "too short")
	require.False(t, ValidMessageID("msg_"+"0123456789abcdef0123456789abcde"), "prefixed message is not raw 32 hex")
	require.False(t, ValidMessageID(strings.ToUpper(id)), "uppercase hex")
}

func TestValidNewEntityIDForm(t *testing.T) {
	t.Parallel()
	for _, prefix := range []string{"usr", "agt", "wfx", "btn", "chn", "thr", "wsc", "tsk", "dm", "wsp"} {
		id, err := NewEntityID(prefix)
		require.NoError(t, err)
		require.True(t, ValidNewEntityIDForm(id, prefix), id)
	}
	require.False(t, ValidNewEntityIDForm("wfx_1", "usr"), "short suffix")
	require.False(t, ValidNewEntityIDForm("noprefix_12345678901234", "usr"), "wrong prefix")
	require.False(t, ValidNewEntityIDForm("usr_1234567890123!", "usr"), "punctuation")
	require.False(t, ValidNewEntityIDForm("", "usr"), "empty")
	// A StableChildID hex form is NOT a NewEntityID base36 form.
	require.False(t, ValidNewEntityIDForm("usr_"+strings.Repeat("a", 32), "usr"), "hex suffix is not base36 entity")
}

func TestValidStableChildIDExact32Hex(t *testing.T) {
	t.Parallel()
	require.True(t, ValidStableChildID("wfx_"+strings.Repeat("a", 32), "wfx"))
	require.True(t, ValidStableChildID("btn_"+strings.Repeat("0", 32), "btn"))
	require.False(t, ValidStableChildID("wfx_1", "wfx"), "short suffix")
	require.False(t, ValidStableChildID("wfx_"+strings.Repeat("a", 31), "wfx"), "31 hex")
	require.False(t, ValidStableChildID("wfx_"+strings.Repeat("g", 32), "wfx"), "non-hex suffix")
	require.False(t, ValidStableChildID("wfx_"+strings.Repeat("A", 32), "wfx"), "uppercase hex")
	require.False(t, ValidStableChildID("wfx_"+strings.Repeat("a", 32)+"!", "wfx"), "punctuation")
	require.False(t, ValidStableChildID("", "wfx"), "empty")
	require.False(t, ValidStableChildID("wfx_"+strings.Repeat("a", 32), "wri"), "wrong prefix")
}
