// Copyright (c) quickfixengine.org  All rights reserved.
//
// This file may be distributed under the terms of the quickfixengine.org
// license as defined by quickfixengine.org and appearing in the file
// LICENSE included in the packaging of this file.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
// THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE.
//
// See http://www.quickfixengine.org/LICENSE for licensing information.
//
// Contact ask@quickfixengine.org if any conditions of this licensing
// are not clear to you.

package quickfix

import (
	"bytes"
	"reflect"
	"testing"

	"github.com/stretchr/testify/suite"

	"quantex.com/qfixdpl/quickfix/datadictionary"
)

func BenchmarkParseMessage(b *testing.B) {
	rawMsg := bytes.NewBufferString("8=FIX.4.29=10435=D34=249=TW52=20140515-19:49:56.65956=ISLD11=10021=140=154=155=TSLA60=00010101-00:00:00.00010=039")

	msg := NewMessage()
	for i := 0; i < b.N; i++ {
		_ = ParseMessage(msg, rawMsg)
	}
}

type MessageSuite struct {
	QuickFIXSuite
	msg *Message
}

func TestMessageSuite(t *testing.T) {
	suite.Run(t, new(MessageSuite))
}

func (s *MessageSuite) SetupTest() {
	s.msg = NewMessage()
}

func TestXMLNonFIX(t *testing.T) {
	rawMsg := bytes.NewBufferString("8=FIX.4.29=37235=n34=25512369=148152=20200522-07:05:33.75649=CME50=G56=OAEAAAN57=TRADE_CAPTURE143=US,IL212=261213=<RTRF>8=FIX.4.29=22535=BZ34=6549369=651852=20200522-07:05:33.74649=CME50=G56=9Q5000N57=DUMMY143=US,IL11=ACP159013113373460=20200522-07:05:33.734533=0893=Y1028=Y1300=991369=99612:325081373=31374=91375=15979=159013113373461769710=167</RTRF>10=245\"")
	msg := NewMessage()
	_ = ParseMessage(msg, rawMsg)

	if !msg.Header.Has(tagXMLData) {
		t.Error("Expected xmldata tag")
	}
}

func (s *MessageSuite) TestParseMessageEmpty() {
	rawMsg := bytes.NewBufferString("")

	err := ParseMessage(s.msg, rawMsg)
	s.NotNil(err)
}

func (s *MessageSuite) TestParseMessage() {
	rawMsg := bytes.NewBufferString("8=FIX.4.29=10435=D34=249=TW52=20140515-19:49:56.65956=ISLD11=10021=140=154=155=TSLA60=00010101-00:00:00.00010=039")

	err := ParseMessage(s.msg, rawMsg)
	s.Nil(err)

	s.True(bytes.Equal(rawMsg.Bytes(), s.msg.rawMessage.Bytes()), "Expected msg bytes to equal raw bytes")

	expectedBodyBytes := []byte("11=10021=140=154=155=TSLA60=00010101-00:00:00.000")

	s.True(bytes.Equal(s.msg.bodyBytes, expectedBodyBytes), "Incorrect body bytes, got %s", string(s.msg.bodyBytes))

	s.Equal(14, len(s.msg.fields))

	msgType, err := s.msg.MsgType()
	s.Nil(err)

	s.Equal("D", msgType)
	s.True(s.msg.IsMsgTypeOf("D"))

	s.False(s.msg.IsMsgTypeOf("A"))
}

func (s *MessageSuite) TestParseMessageWithDataDictionary() {
	dict := new(datadictionary.DataDictionary)
	dict.Header = &datadictionary.MessageDef{
		Fields: map[int]*datadictionary.FieldDef{
			10030: nil,
		},
	}
	dict.Trailer = &datadictionary.MessageDef{
		Fields: map[int]*datadictionary.FieldDef{
			5050: nil,
		},
	}
	rawMsg := bytes.NewBufferString("8=FIX.4.29=12635=D34=249=TW52=20140515-19:49:56.65956=ISLD10030=CUST11=10021=140=154=155=TSLA60=00010101-00:00:00.0005050=HELLO10=039")

	err := ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict)
	s.Nil(err)
	s.FieldEquals(Tag(10030), "CUST", s.msg.Header)
	s.FieldEquals(Tag(5050), "HELLO", s.msg.Trailer)
}

func (s *MessageSuite) TestParseOutOfOrder() {
	// Allow fields out of order, save for validation.
	rawMsg := bytes.NewBufferString("8=FIX.4.09=8135=D11=id21=338=10040=154=155=MSFT34=249=TW52=20140521-22:07:0956=ISLD10=250")
	s.Nil(ParseMessage(s.msg, rawMsg))
}

func (s *MessageSuite) TestBuild() {
	s.msg.Header.SetField(tagBeginString, FIXString(BeginStringFIX44))
	s.msg.Header.SetField(tagMsgType, FIXString("A"))
	s.msg.Header.SetField(tagSendingTime, FIXString("20140615-19:49:56"))

	s.msg.Body.SetField(Tag(553), FIXString("my_user"))
	s.msg.Body.SetField(Tag(554), FIXString("secret"))

	expectedBytes := []byte("8=FIX.4.49=4935=A52=20140615-19:49:56553=my_user554=secret10=072")
	result := s.msg.build()
	s.True(bytes.Equal(expectedBytes, result), "Unexpected bytes, got %s", string(result))
}

func (s *MessageSuite) TestReBuild() {
	rawMsg := bytes.NewBufferString("8=FIX.4.29=10435=D34=249=TW52=20140515-19:49:56.65956=ISLD11=10021=140=154=155=TSLA60=00010101-00:00:00.00010=039")

	s.Nil(ParseMessage(s.msg, rawMsg))

	s.msg.Header.SetField(tagOrigSendingTime, FIXString("20140515-19:49:56.659"))
	s.msg.Header.SetField(tagSendingTime, FIXString("20140615-19:49:56"))
	s.msg.Header.SetField(tagPossDupFlag, FIXBoolean(true))

	rebuildBytes := s.msg.build()

	expectedBytes := []byte("8=FIX.4.29=13135=D34=243=Y49=TW52=20140615-19:49:5656=ISLD122=20140515-19:49:56.65911=10021=140=154=155=TSLA60=00010101-00:00:00.00010=122")

	s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n +%s\n -%s", rebuildBytes, expectedBytes)

	expectedBodyBytes := []byte("11=10021=140=154=155=TSLA60=00010101-00:00:00.000")

	s.True(bytes.Equal(s.msg.bodyBytes, expectedBodyBytes), "Incorrect body bytes, got %s", string(s.msg.bodyBytes))
}

func (s *MessageSuite) TestRebuildOneRepeatingGroupWithDictionary() {
	dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
	s.Nil(dictErr)

	// Given message bytes from a valid string with a 453 repeating group.
	rawMsg := bytes.NewBufferString(
		"8=FIX.4.49=16535=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41453=1448=4501447=D452=28" +
			"10=026")

	// When we parse it into a message
	s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))

	// And then rebuild the message bytes
	rebuildBytes := s.msg.build()
	expectedBytes := []byte(
		"8=FIX.4.49=16535=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41453=1448=4501447=D452=28" +
			"10=026")

	// Then the bytes should have repeating groups properly ordered
	s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
}

func (s *MessageSuite) TestRebuildTwoRepeatingGroupsWithDictionary() {
	dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
	s.Nil(dictErr)

	// Given message bytes from a valid string with a 386 repeating group and a 453 repeating group.
	rawMsg := bytes.NewBufferString("8=FIX.4.49=21035=D34=2347=UTF-852=20231231-20:19:4149=0100150=01001a56=TEST44=1211=139761=1010040021=1386=1336=NOPL55=SYMABC54=160=20231231-20:19:4138=140=259=0453=1448=4501447=D452=28354=6355=Public10=104")

	// When we parse it into a message
	s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))

	// And then rebuild the message bytes
	rebuildBytes := s.msg.build()
	expectedBytes := []byte("8=FIX.4.49=21035=D34=249=0100150=01001a52=20231231-20:19:4156=TEST347=UTF-81=1010040011=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41354=6355=Public386=1336=NOPL453=1448=4501447=D452=2810=104")

	// Then the bytes should have repeating groups properly ordered
	s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
}

func (s *MessageSuite) TestRebuildOneRepeatingGroupWithTwoMembersWithDictionary() {
	dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
	s.Nil(dictErr)

	// Given message bytes from a valid string with a 453 repeating group that has 2 child groups.
	rawMsg := bytes.NewBufferString(
		"8=FIX.4.49=18735=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41453=2448=4501447=D452=28448=4502447=D452=28" +
			"10=044")

	// When we parse it into a message
	s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))

	// And then rebuild the message bytes
	rebuildBytes := s.msg.build()
	expectedBytes := []byte(
		"8=FIX.4.49=18735=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41453=2448=4501447=D452=28448=4502447=D452=28" +
			"10=044")

	// Then the bytes should have repeating groups properly ordered
	s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
}

func (s *MessageSuite) TestRebuildTwoSequentialRepeatingGroupWithDictionary() {
	dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
	s.Nil(dictErr)

	// Given message bytes from a valid string with both a 78 and 453 repeating group that each have 2 child groups.
	rawMsg := bytes.NewBufferString(
		"8=FIX.4.49=21035=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=279=acct179=acct2453=2448=4501447=D452=28448=4502447=D452=28" +
			"10=243")

	// When we parse it into a message
	s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))

	// And then rebuild the message bytes
	rebuildBytes := s.msg.build()
	expectedBytes := []byte(
		"8=FIX.4.49=21035=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=279=acct179=acct2453=2448=4501447=D452=28448=4502447=D452=28" +
			"10=243")

	// Then the bytes should have repeating groups properly ordered
	s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
}

func (s *MessageSuite) TestRebuildNestedRepeatingGroupWithDictionary() {
	dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
	s.Nil(dictErr)

	// Given message bytes from a valid string with a 78 repeating group that has
	// a nested 539 group and then another 80 tag in the 78 group
	rawMsg := bytes.NewBufferString(
		"8=FIX.4.49=17735=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid80=100" +
			"10=206")

	// When we parse it into a message
	s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))

	// And then rebuild the message bytes
	rebuildBytes := s.msg.build()
	expectedBytes := []byte(
		"8=FIX.4.49=17735=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid80=100" +
			"10=206")

	// Then the bytes should have repeating groups properly ordered
	s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
}

func (s *MessageSuite) TestRebuildDoubleNestedRepeatingGroupWithDictionary() {
	dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
	s.Nil(dictErr)

	// Given message bytes from a valid string with a 78 repeating group that has a
	// double nested 539 and then 804 groups and then another 80 tag in the 78 group
	rawMsg := bytes.NewBufferString(
		"8=FIX.4.49=20235=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid80=100" +
			"10=117")

	// When we parse it into a message
	s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))

	// And then rebuild the message bytes
	rebuildBytes := s.msg.build()
	expectedBytes := []byte(
		"8=FIX.4.49=20235=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid80=100" +
			"10=117")

	// Then the bytes should have repeating groups properly ordered
	s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
}

func (s *MessageSuite) TestRebuildDoubleNestedThenAnotherRepeatingGroupWithDictionary() {
	dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
	s.Nil(dictErr)

	// Given message bytes from a valid string with a 78 repeating group that has a double nested 539 and then 804 groups
	// and then another repeating group 453 with two children.
	rawMsg := bytes.NewBufferString(
		"8=FIX.4.49=24535=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid453=2448=4501447=D452=28448=4502447=D452=28" +
			"10=106")

	// When we parse it into a message
	s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))

	// And then rebuild the message bytes
	rebuildBytes := s.msg.build()
	expectedBytes := []byte(
		"8=FIX.4.49=24535=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid453=2448=4501447=D452=28448=4502447=D452=28" +
			"10=106")

	// Then the bytes should have repeating groups properly ordered
	s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
}

func (s *MessageSuite) TestRebuildDoubleNestedThenBodyTagThenAnotherRepeatingGroupWithDictionary() {
	dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
	s.Nil(dictErr)

	// Given message bytes from a valid string with a 78 repeating group that has a double nested 539 and then 804 groups
	// then a 376 body tag and then another repeating group 453 with two children.
	rawMsg := bytes.NewBufferString(
		"8=FIX.4.49=25635=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid376=compid453=2448=4501447=D452=28448=4502447=D452=28" +
			"10=198")

	// When we parse it into a message
	s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))

	// And then rebuild the message bytes
	rebuildBytes := s.msg.build()
	expectedBytes := []byte(
		"8=FIX.4.49=25635=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid376=compid453=2448=4501447=D452=28448=4502447=D452=28" +
			"10=198")

	// Then the bytes should have repeating groups properly ordered
	s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
}

func (s *MessageSuite) TestRebuildDoubleNestedWithTwoMembersRepeatingGroupWithDictionary() {
	dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
	s.Nil(dictErr)

	// Given message bytes from a valid string with a 78 repeating group that
	// has a double nested 539 and then 804 groups all with two children.
	rawMsg := bytes.NewBufferString(
		"8=FIX.4.49=40635=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41" +
			"78=179=acct1" +
			"539=2" +
			"524=nestedid" +
			"804=2" +
			"545=doublenestedid" +
			"545=doublenestedid2" +
			"524=nestedid2" +
			"804=2" +
			"545=doublenestedid" +
			"545=doublenestedid2" +
			"79=acct2" +
			"539=2" +
			"524=nestedid" +
			"804=2" +
			"545=doublenestedid" +
			"545=doublenestedid2" +
			"524=nestedid2" +
			"804=2" +
			"545=doublenestedid" +
			"545=doublenestedid2" +
			"10=046")

	// When we parse it into a message
	s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))

	// And then rebuild the message bytes
	rebuildBytes := s.msg.build()
	expectedBytes := []byte(
		"8=FIX.4.49=40635=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
			"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41" +
			"78=179=acct1539=2524=nestedid804=2545=doublenestedid545=doublenestedid2524=nestedid2804=2545=doublenestedid545=doublenestedid2" +
			"79=acct2539=2524=nestedid804=2545=doublenestedid545=doublenestedid2524=nestedid2804=2545=doublenestedid545=doublenestedid2" +
			"10=046")

	// Then the bytes should have repeating groups properly ordered
	s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
}

func (s *MessageSuite) TestReBuildWithRepeatingGroupForResend() {
	// Given the following message with a repeating group
	origHeader := "8=FIXT.1.19=16135=834=349=ISLD52=20240415-03:43:17.92356=TW"
	origBody := "6=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=1448=xyzzy447=D452=1"
	origTrailer := "10=014"
	rawMsg := bytes.NewBufferString(origHeader + origBody + origTrailer)

	// When I reparse the message from the store during a resend request
	s.Nil(ParseMessage(s.msg, rawMsg))

	// And I update the headers for resend
	s.msg.Header.SetField(tagOrigSendingTime, FIXString("20240415-03:43:17.923"))
	s.msg.Header.SetField(tagSendingTime, FIXString("20240415-14:41:23.456"))
	s.msg.Header.SetField(tagPossDupFlag, FIXBoolean(true))

	// When I rebuild the message
	rebuildBytes := s.msg.build()

	// Then the repeating groups will not be in the correct order in the rebuilt message (note tags 447, 448, 452, 453)
	expectedBytes := []byte("8=FIXT.1.19=19235=834=343=Y49=ISLD52=20240415-14:41:23.45656=TW122=20240415-03:43:17.9236=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=1448=xyzzy447=D452=110=018")
	s.False(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n  but was: %s", expectedBytes, rebuildBytes)
	expectedOutOfOrderBytes := []byte("8=FIXT.1.19=19235=834=343=Y49=ISLD52=20240415-14:41:23.45656=TW122=20240415-03:43:17.9236=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00447=D448=xyzzy452=1453=110=018")
	s.True(bytes.Equal(expectedOutOfOrderBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n  but was: %s", expectedOutOfOrderBytes, rebuildBytes)

	// But the bodyBytes will still be correct
	origBodyBytes := []byte(origBody)
	s.True(bytes.Equal(origBodyBytes, s.msg.bodyBytes), "Incorrect body bytes, \n expected: %s\n  but was: %s", origBodyBytes, s.msg.bodyBytes)

	// So when I combine the updated header + the original bodyBytes + the as-is trailer
	resendBytes := s.msg.buildWithBodyBytes(s.msg.bodyBytes)

	// Then the reparsed, rebuilt message will retain the correct ordering of repeating group tags during resend
	s.True(bytes.Equal(expectedBytes, resendBytes), "Unexpected bytes,\n expected: %s\n  but was: %s", expectedBytes, resendBytes)
}

func (s *MessageSuite) TestReBuildWithRepeatingGroupMultipleEntriesInGroupForResend() {
	// Given the following message with a repeating group that has 2 entries
	origHeader := "8=FIXT.1.19=18435=834=349=ISLD52=20240415-03:43:17.92356=TW"
	origBody := "6=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=2448=xyzzy447=D452=1448=foobar447=D452=3"
	origTrailer := "10=152"
	rawMsg := bytes.NewBufferString(origHeader + origBody + origTrailer)

	// When I reparse the message from the store during a resend request
	s.Nil(ParseMessage(s.msg, rawMsg))

	// And I update the headers for resend
	s.msg.Header.SetField(tagOrigSendingTime, FIXString("20240415-03:43:17.923"))
	s.msg.Header.SetField(tagSendingTime, FIXString("20240415-14:41:23.456"))
	s.msg.Header.SetField(tagPossDupFlag, FIXBoolean(true))

	// The bodyBytes will still be correct
	origBodyBytes := []byte(origBody)
	s.True(bytes.Equal(origBodyBytes, s.msg.bodyBytes), "Incorrect body bytes, \n expected: %s\n  but was: %s", origBodyBytes, s.msg.bodyBytes)

	// So when I combine the updated header + the original bodyBytes + the as-is trailer
	resendBytes := s.msg.buildWithBodyBytes(origBodyBytes)

	// Then the reparsed, rebuilt message will retain the correct ordering of repeating group tags during resend
	expectedResendHeader := "8=FIXT.1.19=21535=834=343=Y49=ISLD52=20240415-14:41:23.45656=TW122=20240415-03:43:17.923"
	expectedResendBody := "6=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=2448=xyzzy447=D452=1448=foobar447=D452=3"
	expectedResendTrailer := "10=147"
	expectedResendBytes := []byte(expectedResendHeader + expectedResendBody + expectedResendTrailer)
	s.True(bytes.Equal(expectedResendBytes, resendBytes), "Unexpected bytes,\n expected: %s\n  but was: %s", expectedResendBytes, resendBytes)
}

func (s *MessageSuite) TestReverseRoute() {
	s.Nil(ParseMessage(s.msg, bytes.NewBufferString("8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=123")))

	builder := s.msg.reverseRoute()

	var testCases = []struct {
		tag           Tag
		expectedValue string
	}{
		{tagTargetCompID, "TW"},
		{tagTargetSubID, "KK"},
		{tagTargetLocationID, "JV"},
		{tagSenderCompID, "ISLD"},
		{tagSenderSubID, "AP"},
		{tagSenderLocationID, "RY"},
		{tagDeliverToCompID, "JCD"},
		{tagDeliverToSubID, "CS"},
		{tagDeliverToLocationID, "BB"},
		{tagOnBehalfOfCompID, "MG"},
		{tagOnBehalfOfSubID, "CB"},
		{tagOnBehalfOfLocationID, "BH"},
	}

	for _, tc := range testCases {
		var field FIXString
		s.Nil(builder.Header.GetField(tc.tag, &field))

		s.Equal(tc.expectedValue, string(field))
	}
}

func (s *MessageSuite) TestReverseRouteIgnoreEmpty() {
	s.Nil(ParseMessage(s.msg, bytes.NewBufferString("8=FIX.4.09=12835=D34=249=TW52=20060102-15:04:0556=ISLD115=116=CS128=MG129=CB11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=123")))
	builder := s.msg.reverseRoute()

	s.False(builder.Header.Has(tagDeliverToCompID), "Should not reverse if empty")
}

func (s *MessageSuite) TestReverseRouteFIX40() {
	// The onbehalfof/deliverto location id not supported in fix 4.0.
	s.Nil(ParseMessage(s.msg, bytes.NewBufferString("8=FIX.4.09=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=123")))

	builder := s.msg.reverseRoute()

	s.False(builder.Header.Has(tagDeliverToLocationID), "delivertolocation id not supported in fix40")

	s.False(builder.Header.Has(tagOnBehalfOfLocationID), "onbehalfof location id not supported in fix40")
}

func (s *MessageSuite) TestCopyIntoMessage() {
	msgString := "8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=123"
	msgBuf := bytes.NewBufferString(msgString)
	s.Nil(ParseMessage(s.msg, msgBuf))

	dest := NewMessage()
	s.msg.CopyInto(dest)

	checkFieldInt(s, dest.Header.FieldMap, int(tagMsgSeqNum), 2)
	checkFieldInt(s, dest.Body.FieldMap, 21, 3)
	checkFieldString(s, dest.Body.FieldMap, 11, "ID")
	s.Equal(len(dest.bodyBytes), len(s.msg.bodyBytes))

	// copying decouples the message from its input buffer, so the raw message will be re-rendered
	renderedString := "8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP115=JCD116=CS128=MG129=CB142=JV143=RY144=BB145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=033"
	s.Equal(dest.String(), renderedString)

	s.True(reflect.DeepEqual(s.msg.bodyBytes, dest.bodyBytes))
	s.True(s.msg.IsMsgTypeOf("D"))
	s.Equal(s.msg.ReceiveTime, dest.ReceiveTime)

	s.True(reflect.DeepEqual(s.msg.fields, dest.fields))

	// update the source message to validate the copy is truly deep
	newMsgString := "8=FIX.4.49=4935=A52=20140615-19:49:56553=my_user554=secret10=072"
	s.Nil(ParseMessage(s.msg, bytes.NewBufferString(newMsgString)))
	s.True(s.msg.IsMsgTypeOf("A"))
	s.Equal(s.msg.String(), newMsgString)
	s.Equal(string(s.msg.Bytes()), newMsgString)

	// clear the source buffer also
	msgBuf.Reset()

	s.True(dest.IsMsgTypeOf("D"))
	s.Equal(dest.String(), renderedString)
	s.Equal(string(dest.Bytes()), renderedString)
}

func checkFieldInt(s *MessageSuite, fields FieldMap, tag, expected int) {
	toCheck, _ := fields.GetInt(Tag(tag))
	s.Equal(expected, toCheck)
}

func checkFieldString(s *MessageSuite, fields FieldMap, tag int, expected string) {
	toCheck, err := fields.GetString(Tag(tag))
	s.NoError(err)
	s.Equal(expected, toCheck)
}
