package message_test

import (
	"testing"

	"code.justin.tv/cplat/twitchling/localization/language"
	"code.justin.tv/cplat/twitchling/localization/message"
	"github.com/stretchr/testify/assert"
)

func TestFormat(t *testing.T) {
	t.Run("returns text fragment for simple text input", func(t *testing.T) {
		input := "this is test text"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(nil)
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		textFragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, input, textFragment.Value())
		assert.False(t, textFragment.HasEmphasis())
	})

	t.Run("returns text fragment with emphasis for text with emphasis element", func(t *testing.T) {
		input := "<em>this is test text</em>"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(nil)
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		textFragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "this is test text", textFragment.Value())
		assert.True(t, textFragment.HasEmphasis())
	})

	t.Run("returns text fragments with basic string placeholder", func(t *testing.T) {
		input := "the input was: {testArg}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewString("hello"),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 2)

		textFragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "the input was: ", textFragment.Value())
		assert.False(t, textFragment.HasEmphasis())

		textFragment, ok = result[1].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "hello", textFragment.Value())
		assert.False(t, textFragment.HasEmphasis())
	})

	t.Run("returns text fragments with basic string placeholder including emphasis", func(t *testing.T) {
		input := "<em>the input was: {testArg}</em>"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewString("hello"),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 2)

		textFragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "the input was: ", textFragment.Value())
		assert.True(t, textFragment.HasEmphasis())

		textFragment, ok = result[1].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "hello", textFragment.Value())
		assert.True(t, textFragment.HasEmphasis())
	})

	t.Run("returns string fragment from nested message", func(t *testing.T) {
		input := "{testArg}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		inner, err := message.New(language.DefaultLanguage, "{text}")
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNestedMessage(inner, message.PlaceholderMap{
				"text": message.NewString("test text"),
			}),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "test text", fragment.Value())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns string fragment from nested message inside emphasis", func(t *testing.T) {
		input := "<em>{testArg}</em>"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		inner, err := message.New(language.DefaultLanguage, "{text}")
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNestedMessage(inner, message.PlaceholderMap{
				"text": message.NewString("test text"),
			}),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "test text", fragment.Value())
		assert.True(t, fragment.HasEmphasis())
	})

	t.Run("returns number fragment with basic string placeholder", func(t *testing.T) {
		input := "{testArg}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(42),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.NumberFragment)
		assert.True(t, ok)
		assert.Equal(t, 42.0, fragment.Value())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns error when placeholder can't be found", func(t *testing.T) {
		input := "{argName}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		_, err = messageFormat.Format(nil)
		assert.Error(t, err)
		_, ok := err.(*message.PlaceholderError)
		assert.True(t, ok)
	})

	t.Run("returns ID fragment with kind of UserID with basic string placeholder", func(t *testing.T) {
		input := "{user}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"user": message.NewUserID("1234"),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.IDFragment)
		assert.True(t, ok)
		assert.Equal(t, "1234", fragment.Value())
		assert.Equal(t, message.IDFragmentKind_User, fragment.Kind())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns ID fragment with kind of EmoteID with basic string placeholder", func(t *testing.T) {
		input := "{emote}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"emote": message.NewEmoteID("1234"),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.IDFragment)
		assert.True(t, ok)
		assert.Equal(t, "1234", fragment.Value())
		assert.Equal(t, message.IDFragmentKind_Emote, fragment.Kind())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns number fragment with format of integer with number placeholder", func(t *testing.T) {
		input := "{testArg, number}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(42),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.NumberFragment)
		assert.True(t, ok)
		assert.Equal(t, 42.0, fragment.Value())
		assert.Equal(t, message.NumberFragmentKind_Integer, fragment.Kind())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns number fragment with format of integer with number placeholder with integer format", func(t *testing.T) {
		input := "{testArg, number, integer}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(42),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.NumberFragment)
		assert.True(t, ok)
		assert.Equal(t, 42.0, fragment.Value())
		assert.Equal(t, message.NumberFragmentKind_Integer, fragment.Kind())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns number fragment with format of currency with number placeholder with currency format", func(t *testing.T) {
		input := "{testArg, number, currency}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(42),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.NumberFragment)
		assert.True(t, ok)
		assert.Equal(t, 42.0, fragment.Value())
		assert.Equal(t, message.NumberFragmentKind_Currency, fragment.Kind())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns number fragment with format of percent with number placeholder with percent format", func(t *testing.T) {
		input := "{testArg, number, percent}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(1),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.NumberFragment)
		assert.True(t, ok)
		assert.Equal(t, 1.0, fragment.Value())
		assert.Equal(t, message.NumberFragmentKind_Percent, fragment.Kind())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns error for number format functions with invalid placeholder type", func(t *testing.T) {
		input := "{testArg, number, integer}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewString("test"),
		})
		assert.Error(t, err)
		assert.Len(t, result, 0)
	})

	t.Run("returns error for number format functions with missing placeholder", func(t *testing.T) {
		input := "{testArg, number, integer}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{})
		assert.Error(t, err)
		assert.Len(t, result, 0)
	})

	t.Run("returns other plural variant for N != 1 for en-US", func(t *testing.T) {
		input := "{testArg, plural, one {One} other {Other}}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(10),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "Other", fragment.Value())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns one plural variant for N == 1 for en-US", func(t *testing.T) {
		input := "{testArg, plural, one {One} other {Other}}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(1),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "One", fragment.Value())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns other plural variant for N == 1.30 for en-US", func(t *testing.T) {
		input := "{testArg, plural, one {One} other {Other}}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(1.30),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "Other", fragment.Value())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns other plural variant for N == -50 for en-US", func(t *testing.T) {
		input := "{testArg, plural, one {One} other {Other}}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(-50),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "Other", fragment.Value())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns few plural variant for N == 2 for sk-SK", func(t *testing.T) {
		input := "{testArg, plural, one {One} few {Few} other {Other}}"
		messageFormat, err := message.New(unwrapTag(language.NewTag("sk-SK")), input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(2),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "Few", fragment.Value())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns one plural variant for N == 21 for ru-RU", func(t *testing.T) {
		input := "{testArg, plural, one {One} other {Other}}"
		messageFormat, err := message.New(unwrapTag(language.NewTag("ru-RU")), input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(21),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.TextFragment)
		assert.True(t, ok)
		assert.Equal(t, "One", fragment.Value())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns argument value in place of # for plurals", func(t *testing.T) {
		input := "{testArg, plural, other {#}}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(10),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.NumberFragment)
		assert.True(t, ok)
		assert.Equal(t, 10.0, fragment.Value())
		assert.False(t, fragment.HasEmphasis())
	})

	t.Run("returns argument value with emphasis when encased in <em> element for plurals", func(t *testing.T) {
		input := "{testArg, plural, other {<em>#</em>}}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewNumber(10),
		})
		assert.NoError(t, err)
		assert.Len(t, result, 1)

		fragment, ok := result[0].(*message.NumberFragment)
		assert.True(t, ok)
		assert.Equal(t, 10.0, fragment.Value())
		assert.True(t, fragment.HasEmphasis())
	})

	t.Run("returns error when plural argument is not a number", func(t *testing.T) {
		input := "{testArg, plural, other {#}}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{
			"testArg": message.NewString("test"),
		})
		assert.Error(t, err)
		assert.Len(t, result, 0)
	})

	t.Run("returns error when plural argument is missing from placeholders", func(t *testing.T) {
		input := "{testArg, plural, other {#}}"
		messageFormat, err := message.New(language.DefaultLanguage, input)
		assert.NoError(t, err)

		result, err := messageFormat.Format(message.PlaceholderMap{})
		assert.Error(t, err)
		assert.Len(t, result, 0)
	})
}

func unwrapTag(tag language.Tag, err error) language.Tag {
	if err != nil {
		panic(err)
	}
	return language.Tag(tag.Inner())
}
