package domain

import (
	"encoding/json"
	"testing"

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

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

	for _, scope := range []MessageScopeValue{
		{Kind: MessageScopeKindChannel, ScopeID: "chn_abcdefghijklmn"},
		{Kind: MessageScopeKindThread, ScopeID: "chn_bcdefghijklmno"},
		{Kind: MessageScopeKindDM, ScopeID: "dm_cdefghijklmnop"},
		{Kind: MessageScopeKindTask, ScopeID: "tsk_defghijklmnopq"},
	} {
		scope := scope
		t.Run(string(scope.Kind), func(t *testing.T) {
			t.Parallel()

			normalized, err := NormalizeMessageScopeValue(scope)
			require.NoError(t, err)
			require.Equal(t, scope, normalized)

			payload, err := json.Marshal(normalized)
			require.NoError(t, err)
			require.JSONEq(t, `{"kind":"`+string(scope.Kind)+`","scopeId":"`+scope.ScopeID+`"}`, string(payload))

			var object map[string]any
			require.NoError(t, json.Unmarshal(payload, &object))
			normalized, err = NormalizeMessageScopeValue(object)
			require.NoError(t, err)
			require.Equal(t, scope, normalized)
		})
	}
}

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

	tests := []struct {
		name  string
		value any
		error string
	}{
		{name: "target string", value: "#all", error: "message scope must be an object with kind and scopeId"},
		{name: "channel string", value: "chn_abcdefghijklmn", error: "message scope must be an object with kind and scopeId"},
		{name: "missing kind", value: map[string]any{"scopeId": "chn_abcdefghijklmn"}, error: "message scope must contain exactly kind and scopeId"},
		{name: "unknown field", value: map[string]any{"kind": "channel", "scopeId": "chn_abcdefghijklmn", "target": "#all"}, error: "message scope must contain exactly kind and scopeId"},
		{name: "wrong field type", value: map[string]any{"kind": "channel", "scopeId": 42}, error: "message scope kind and scopeId must be strings"},
		{name: "team", value: map[string]any{"kind": "team", "scopeId": "chn_abcdefghijklmn"}, error: `unsupported message scope kind "team"`},
		{name: "reply thread alias", value: map[string]any{"kind": "reply_thread", "scopeId": "chn_abcdefghijklmn"}, error: `unsupported message scope kind "reply_thread"`},
		{name: "kind whitespace", value: map[string]any{"kind": " channel", "scopeId": "chn_abcdefghijklmn"}, error: "message scope kind must not have surrounding whitespace"},
		{name: "id whitespace", value: map[string]any{"kind": "channel", "scopeId": " chn_abcdefghijklmn"}, error: "message scope id must not have surrounding whitespace"},
		{name: "id casing", value: map[string]any{"kind": "channel", "scopeId": "chn_Abcdefghijklmn"}, error: `invalid message scope id "chn_Abcdefghijklmn"`},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			t.Parallel()

			_, err := NormalizeMessageScopeValue(test.value)
			require.EqualError(t, err, test.error)
			require.ErrorIs(t, err, ErrInvalidArgument)
		})
	}
}

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

	tests := []struct {
		target ResolvedMessageTarget
		want   MessageScopeValue
	}{
		{
			target: ResolvedMessageTarget{Kind: MessageTargetKindChannel, ScopeID: "chn_abcdefghijklmn"},
			want:   MessageScopeValue{Kind: MessageScopeKindChannel, ScopeID: "chn_abcdefghijklmn"},
		},
		{
			target: ResolvedMessageTarget{Kind: MessageTargetKindReplyThread, ScopeID: "chn_bcdefghijklmno"},
			want:   MessageScopeValue{Kind: MessageScopeKindThread, ScopeID: "chn_bcdefghijklmno"},
		},
		{
			target: ResolvedMessageTarget{Kind: MessageTargetKindDM, ScopeID: "dm_cdefghijklmnop"},
			want:   MessageScopeValue{Kind: MessageScopeKindDM, ScopeID: "dm_cdefghijklmnop"},
		},
		{
			target: ResolvedMessageTarget{Kind: MessageTargetKindTask, ScopeID: "tsk_defghijklmnopq"},
			want:   MessageScopeValue{Kind: MessageScopeKindTask, ScopeID: "tsk_defghijklmnopq"},
		},
	}
	for _, test := range tests {
		got, err := MessageScopeValueFromResolvedTarget(test.target)
		require.NoError(t, err)
		require.Equal(t, test.want, got)
	}

	_, err := MessageScopeValueFromResolvedTarget(ResolvedMessageTarget{
		Kind: MessageTargetKindTeam, ScopeID: "chn_abcdefghijklmn",
	})
	require.EqualError(t, err, `resolved message target kind "team" is not a concrete message scope`)
	require.ErrorIs(t, err, ErrInvalidArgument)
}
