package domain

import (
	"testing"

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

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

	reference, err := ParseThreadReference("#all")
	require.NoError(t, err)
	require.Equal(t, ThreadTypeChannel, reference.Type)
	require.Equal(t, "all", reference.ChannelSlug)
	require.Equal(t, "#all", reference.String())
}

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

	reference, err := ParseThreadReference("DM:@m8x2ra")
	require.NoError(t, err)
	require.Equal(t, ThreadTypeDM, reference.Type)
	require.Equal(t, "m8x2ra", reference.PeerMemberID)
	require.Equal(t, "dm:@m8x2ra", reference.String())
}

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

	reference, err := ParseThreadReference("dm:@alpha-agent")
	require.NoError(t, err)
	require.Equal(t, ThreadTypeDM, reference.Type)
	require.Equal(t, "alpha-agent", reference.PeerMemberID)
	require.Equal(t, "dm:@alpha-agent", reference.String())
}

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

	reference, err := ParseThreadReference("dm:@user_9eb21fc368")
	require.NoError(t, err)
	require.Equal(t, ThreadTypeDM, reference.Type)
	require.Equal(t, "user_9eb21fc368", reference.PeerMemberID)
	require.Equal(t, "dm:@user_9eb21fc368", reference.String())

	threadReference, err := ParseThreadReference("dm:@mem_private_owner:AbCd1234")
	require.NoError(t, err)
	require.Equal(t, ThreadTypeDM, threadReference.Type)
	require.Equal(t, "mem_private_owner", threadReference.PeerMemberID)
	require.Equal(t, "abcd1234", threadReference.ShortID)
	require.Equal(t, "dm:@mem_private_owner:abcd1234", threadReference.String())
}

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

	_, err := ParseThreadReference("general")
	require.EqualError(t, err, "invalid channel identifier")
	require.ErrorIs(t, err, ErrInvalidArgument)

	_, err = ParseThreadReference("DM:@Bad-ID")
	require.EqualError(t, err, "invalid member reference \"Bad-ID\"")
	require.ErrorIs(t, err, ErrInvalidArgument)
}

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

	_, err := ParseThreadReference("[DM:@m8x2ra]")
	require.EqualError(t, err, "invalid channel identifier")
	require.ErrorIs(t, err, ErrInvalidArgument)
}

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

	reference, err := ParseThreadReference("#all:abcd1234")
	require.NoError(t, err)
	require.Equal(t, ThreadTypeChannel, reference.Type)
	require.Equal(t, "all", reference.ChannelSlug)
	require.Equal(t, "abcd1234", reference.ShortID)
	require.Equal(t, "#all:abcd1234", reference.String())
}

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

	reference, err := ParseThreadReference("dm:@m8x2ra:AbCd1234")
	require.NoError(t, err)
	require.Equal(t, ThreadTypeDM, reference.Type)
	require.Equal(t, "m8x2ra", reference.PeerMemberID)
	require.Equal(t, "abcd1234", reference.ShortID)
	require.Equal(t, "dm:@m8x2ra:abcd1234", reference.String())
}

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

	_, err := ParseThreadReference("#all:")
	require.EqualError(t, err, "invalid thread identifier")
	require.ErrorIs(t, err, ErrInvalidArgument)

	_, err = ParseThreadReference("#all:zzzzzzzz")
	require.EqualError(t, err, "invalid thread identifier")
	require.ErrorIs(t, err, ErrInvalidArgument)
}

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

	scopeID, ok, err := ParseRawScopeTarget("#tsk_abc123")
	require.NoError(t, err)
	require.True(t, ok)
	require.Equal(t, "tsk_abc123", scopeID)

	_, ok, err = ParseRawScopeTarget("#all")
	require.NoError(t, err)
	require.False(t, ok)

	_, ok, err = ParseRawScopeTarget("#tsk_abc123:deadbeef")
	require.Error(t, err)
	require.True(t, ok)

	_, ok, err = ParseRawScopeTarget("#team_abcdefghijklmn")
	require.NoError(t, err)
	require.False(t, ok)

	_, ok, err = ParseRawScopeTarget("#tsk_Bad")
	require.Error(t, err)
	require.True(t, ok)
	require.ErrorIs(t, err, ErrInvalidArgument)
}

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

	tests := []struct {
		name   string
		target string
		kind   MessageTargetKind
		want   string
	}{
		{name: "channel", target: "#all", kind: MessageTargetKindChannel, want: "#all"},
		{name: "channel reply", target: "#all:AbCd1234", kind: MessageTargetKindReplyThread, want: "#all:abcd1234"},
		{name: "dm", target: "DM:@alpha-agent", kind: MessageTargetKindDM, want: "dm:@alpha-agent"},
		{name: "dm reply", target: "dm:@alpha-agent:AbCd1234", kind: MessageTargetKindReplyThread, want: "dm:@alpha-agent:abcd1234"},
		{name: "task", target: "#tsk_abc123", kind: MessageTargetKindTask, want: "#tsk_abc123"},
		{name: "team", target: "#team_abcdefghijklmn", kind: MessageTargetKindTeam, want: "#team_abcdefghijklmn"},
		{name: "team reply", target: "#team_abcdefghijklmn:AbCd1234", kind: MessageTargetKindTeam, want: "#team_abcdefghijklmn:abcd1234"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			t.Parallel()
			reference, err := ParseMessageTargetReference(test.target)
			require.NoError(t, err)
			require.Equal(t, test.kind, reference.Kind)
			require.Equal(t, test.want, reference.String())
		})
	}
}

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

	tests := []struct {
		target string
		error  string
	}{
		{target: "#tsk_", error: `invalid scope id "tsk_"`},
		{target: "#tsk_Bad", error: `invalid scope id "tsk_Bad"`},
		{target: "#tsk_abc123:deadbeef", error: `invalid scope id "tsk_abc123:deadbeef"`},
		{target: "#team_short", error: `invalid team target "team_short"`},
		{target: "#team_", error: `invalid team target "team_"`},
	}
	for _, test := range tests {
		_, err := ParseMessageTargetReference(test.target)
		require.EqualError(t, err, test.error)
		require.ErrorIs(t, err, ErrInvalidArgument)
	}
}

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

	reference, ok, err := ParseTeamTargetReference("#team_abcdefghijklmn")
	require.NoError(t, err)
	require.True(t, ok)
	require.Equal(t, "team_abcdefghijklmn", reference.TeamID)
	require.Empty(t, reference.ShortID)
	require.Equal(t, "#team_abcdefghijklmn", reference.String())

	reference, ok, err = ParseTeamTargetReference("#team_abcdefghijklmn:AbCd1234")
	require.NoError(t, err)
	require.True(t, ok)
	require.Equal(t, "team_abcdefghijklmn", reference.TeamID)
	require.Equal(t, "abcd1234", reference.ShortID)
	require.Equal(t, "#team_abcdefghijklmn:abcd1234", reference.String())

	_, ok, err = ParseTeamTargetReference("#all")
	require.NoError(t, err)
	require.False(t, ok)

	_, ok, err = ParseTeamTargetReference("#team_short")
	require.Error(t, err)
	require.True(t, ok)
	require.ErrorIs(t, err, ErrInvalidArgument)
}

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

	err := ValidateWorkspaceSlug(" all")
	require.EqualError(t, err, "workspace slug must not have surrounding whitespace")
	require.ErrorIs(t, err, ErrInvalidArgument)
}

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

	err := ValidateWorkspaceSlug("Bad-ID")
	require.EqualError(t, err, "invalid workspace slug \"Bad-ID\"")
	require.ErrorIs(t, err, ErrInvalidArgument)
}
