package com.intrasoft.ermis.transit.cargoofficeofdestination.core.service;

import com.intrasoft.ermis.cargov1.messages.CC056CType;
import com.intrasoft.ermis.cargov1.messages.CC057CType;
import com.intrasoft.ermis.cargov1.messages.FunctionalErrorType04;
import com.intrasoft.ermis.cargov1.messages.MessageTypes;
import com.intrasoft.ermis.cargov1.messages.TB007CType;
import com.intrasoft.ermis.cargov1.messages.TB014CType;
import com.intrasoft.ermis.cargov1.messages.TB015CType;
import com.intrasoft.ermis.cargov1.messages.TB028CType;
import com.intrasoft.ermis.common.logging.ErmisLogger;
import com.intrasoft.ermis.common.xml.enums.NamespacesEnum;
import com.intrasoft.ermis.common.xml.parsing.XmlConverter;
import com.intrasoft.ermis.transit.cargoofficeofdestination.core.outbound.MessageProducer;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Declaration;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Message;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.ValidationResult;
import com.intrasoft.ermis.transit.common.config.services.GetCountryCodeService;
import com.intrasoft.ermis.transit.common.enums.DirectionEnum;
import com.intrasoft.ermis.transit.common.enums.MessageDomainTypeEnum;
import com.intrasoft.ermis.transit.common.exceptions.NotFoundException;
import com.intrasoft.ermis.transit.common.mappers.CargoMessageTypeMapper;
import com.intrasoft.ermis.transit.common.util.MessageUtil;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Objects;
import javax.xml.bind.JAXBException;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;

@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class GenerateMessageServiceImpl implements GenerateMessageService {

    private static final String ARRIVAL_PRESENTATION_TIMER_RULE_ID = "CARGO_ARRIVAL_PRESENTATION_TIMER_EXPIRATION";

    private static final String NOT_AVAILABLE = "N/A";

    private final ErmisLogger logger;

    private final DossierDataService dossierDataService;

    private final MessageProducer messageProducer;

    private final GetCountryCodeService getCountryCodeService;

    private final CargoMessageTypeMapper cargoMessageTypeMapper;

    @Override
    public <T> void generateIE056(String movementId, String messageId, Class<T> messageClass) throws NotFoundException, JAXBException {

        Message correlatedMessage;
        List<ValidationResult> validationResultList;

        try {
            correlatedMessage = dossierDataService.getMessageById(messageId);
        } catch (NotFoundException exception) {
            throw new NotFoundException("generateIE056() : Failed to find Message with ID: " + messageId + " for Movement with ID: " + movementId);
        }
        // Find validation results:
        validationResultList = dossierDataService.findValidationResultListByMessageId(messageId);

        // Create CC056C:
        CC056CType cc056cTypeMessage = createCC056CMessage(movementId, validationResultList, correlatedMessage.getPayload(), messageClass);

        // Transform to XML message:
        String cc056cTypeMessageXML = XmlConverter.marshal(cc056cTypeMessage, NamespacesEnum.NTA.getValue(), MessageTypes.CC_056_C.value());

        Message message = Message.builder()
                                 .messageIdentification(cc056cTypeMessage.getMessageIdentification())
                                 .messageType(cc056cTypeMessage.getMessageType().value())
                                 .source(sanitizeInput(cc056cTypeMessage.getMessageSender()))
                                 .destination(sanitizeInput(cc056cTypeMessage.getMessageRecipient()))
                                 .domain(MessageDomainTypeEnum.EXTERNAL.name())
                                 .payload(cc056cTypeMessageXML)
                                 .direction(DirectionEnum.SENT.getDirection())
                                 .build();

        saveAndPublishMessage(message, movementId);
    }

    @Override
    public <T> void generateIE057(String movementId, String messageId, Class<T> messageClass) throws NotFoundException, JAXBException {

        Message correlatedMessage;
        List<ValidationResult> validationResultList;

        try {
            correlatedMessage = dossierDataService.getMessageById(messageId);
        } catch (NotFoundException exception) {
            throw new NotFoundException("generateIE057() : Failed to find Message with ID: " + messageId + " for Movement with ID: " + movementId);
        }
        // Find validation results:
        validationResultList = dossierDataService.findValidationResultListByMessageId(messageId);

        // Create CC057C:
        CC057CType cc057cTypeMessage = createCC057CMessage(validationResultList, correlatedMessage.getPayload(), messageClass);

        // Transform to XML message:
        String cc057cTypeMessageXML = XmlConverter.marshal(cc057cTypeMessage, NamespacesEnum.NTA.getValue(), MessageTypes.CC_057_C.value());

        Message message = Message.builder()
                                 .messageIdentification(cc057cTypeMessage.getMessageIdentification())
                                 .messageType(cc057cTypeMessage.getMessageType().value())
                                 .source(sanitizeInput(cc057cTypeMessage.getMessageSender()))
                                 .destination(sanitizeInput(cc057cTypeMessage.getMessageRecipient()))
                                 .domain(MessageDomainTypeEnum.EXTERNAL.name())
                                 .payload(cc057cTypeMessageXML)
                                 .direction(DirectionEnum.SENT.getDirection())
                                 .build();

        saveAndPublishMessage(message, movementId);
    }

    @Override
    public void generateTB028(String movementId, String mrn) throws JAXBException {

        Declaration declaration = dossierDataService.getLatestDeclarationByMovementId(movementId);

        TB015CType tb015cTypeMessage = XmlConverter.buildMessageFromXML(declaration.getPayload(), TB015CType.class);

        // Create TB028C:
        TB028CType tb028cTypeMessage = createTB028CMessage(tb015cTypeMessage, mrn);

        // Transform to XML message:
        String tb028cTypeMessageXML = XmlConverter.marshal(tb028cTypeMessage, NamespacesEnum.NTA.getValue(), MessageTypes.TB_028_C.value());

        Message message = Message.builder()
                                 .messageIdentification(tb028cTypeMessage.getMessageIdentification())
                                 .messageType(tb028cTypeMessage.getMessageType().value())
                                 .source(tb028cTypeMessage.getMessageSender())
                                 .destination(tb028cTypeMessage.getMessageRecipient())
                                 .domain(MessageDomainTypeEnum.EXTERNAL.name())
                                 .payload(tb028cTypeMessageXML)
                                 .direction(DirectionEnum.SENT.getDirection())
                                 .build();

        saveAndPublishMessage(message, movementId);
    }

    private <T> CC056CType createCC056CMessage(String movementId, List<ValidationResult> validationResults, String messagePayload, Class<T> messageClass)
            throws JAXBException {

        Document document = XmlConverter.convertStringToXMLDocument(messagePayload);
        T messageIn = XmlConverter.buildMessageFromXML(messagePayload, messageClass);
        CC056CType messageOut = new CC056CType();

        if (messageIn instanceof TB015CType) {
            messageOut = cargoMessageTypeMapper.fromTB015CtoCC056C((TB015CType) messageIn);
        } else if (messageIn instanceof TB014CType) {
            Declaration declaration = dossierDataService.getLatestDeclarationByMovementId(movementId);
            TB015CType cc015cTypeMessage = XmlConverter.buildMessageFromXML(declaration.getPayload(), TB015CType.class);
            messageOut = cargoMessageTypeMapper.fromTB014CtoCC056C((TB014CType) messageIn);
            messageOut.setMessageRecipient(cc015cTypeMessage.getMessageSender());
        }

        setCoreDataToMessage(messageOut, messageIn);

        CC056CType finalMessageOut = messageOut;

        if (validationResults.stream().anyMatch(validationResult ->
                validationResult.isRejecting() && ARRIVAL_PRESENTATION_TIMER_RULE_ID.equals(validationResult.getRuleId()))) {
            finalMessageOut.getTransitOperation().setRejectionCode("4");
            finalMessageOut.getTransitOperation().setRejectionReason("Arrival Presentation Timer Expiration");
        } else {
            finalMessageOut.getTransitOperation().setRejectionCode("12");
            finalMessageOut.getTransitOperation().setRejectionReason(null);

            validationResults.stream().filter(ValidationResult::isRejecting).forEach(validationResult -> {
                String originalValue = null;
                try {
                    int valuesArraySize = XmlConverter.evaluateXPath(document, validationResult.getPointer()).size();
                    if (valuesArraySize > 0) {
                        originalValue = XmlConverter.evaluateXPath(document, validationResult.getPointer()).get(0);
                    }
                } catch (Exception e) {
                    logger.error("createCC056Message error: ", e);
                }
                finalMessageOut.getFunctionalError().add(createFunctionalError(validationResult, originalValue));

            });
        }

        return finalMessageOut;
    }

    private <T> CC057CType createCC057CMessage(List<ValidationResult> validationResults, String messagePayload, Class<T> messageClass) throws JAXBException {

        Document document = XmlConverter.convertStringToXMLDocument(messagePayload);
        T messageIn = XmlConverter.buildMessageFromXML(messagePayload, messageClass);
        CC057CType messageOut = new CC057CType();

        if (messageIn instanceof TB007CType) {
            messageOut = cargoMessageTypeMapper.fromTB007CtoCC057C((TB007CType) messageIn);
        }

        setCoreDataToMessage(messageOut, messageIn);

        CC057CType finalMessageOut = messageOut;
        finalMessageOut.getTransitOperation().setRejectionCode("12");
        finalMessageOut.getTransitOperation().setRejectionReason(null);

        validationResults.stream().filter(ValidationResult::isRejecting).forEach(validationResult -> {
            String originalValue = null;
            try {
                int valuesArraySize = XmlConverter.evaluateXPath(document, validationResult.getPointer()).size();
                if (valuesArraySize > 0) {
                    originalValue = XmlConverter.evaluateXPath(document, validationResult.getPointer()).get(0);
                }
            } catch (Exception e) {
                logger.error("createCC057Message error: ", e);
            }
            finalMessageOut.getFunctionalError().add(createFunctionalError(validationResult, originalValue));
        });

        return finalMessageOut;
    }

    private TB028CType createTB028CMessage(TB015CType messageIn, String mrn) {

        TB028CType messageOut = cargoMessageTypeMapper.fromTB015CtoTB028C(messageIn);
        messageOut.setMessageType(MessageTypes.TB_028_C);
        messageOut.setMessageSender(generateMessageSender());
        messageOut.setPreparationDateAndTime(nowUTC());
        messageOut.setMessageIdentification(MessageUtil.generateMessageIdentification());
        messageOut.getTransitOperation().setLRN(messageIn.getTransitOperation().getLRN());
        messageOut.getTransitOperation().setDeclarationAcceptanceDate(nowUTC().toLocalDate());
        messageOut.getTransitOperation().setMRN(mrn);

        return messageOut;
    }

    private void saveAndPublishMessage(Message message, String movementId) {

        Message savedMessage = dossierDataService.saveMessage(message);

        if (!StringUtils.isBlank(movementId)) {
            dossierDataService.associateMessageWithMovement(savedMessage.getId(), movementId);
        }

        messageProducer.publish(message.getPayload(), movementId, message.getMessageType());
    }

    private String generateMessageSender() {
        return CargoMessageTypeMapper.NTA + getCountryCodeService.getCountryCode();
    }

    private LocalDateTime nowUTC() {
        return LocalDateTime.now(ZoneOffset.UTC);
    }

    private FunctionalErrorType04 createFunctionalError(ValidationResult validationResult, String originalValue) {

        FunctionalErrorType04 functionalError = new FunctionalErrorType04();
        functionalError.setErrorPointer(validationResult.getPointer());
        functionalError.setErrorCode(validationResult.getErrorCode());
        functionalError.setErrorReason(validationResult.getRuleId());
        functionalError.setOriginalAttributeValue(originalValue);

        return functionalError;
    }

    private <T> void setCoreDataToMessage(T messageOut, T messageIn) {

        if (messageOut instanceof CC056CType) {
            ((CC056CType) messageOut).setMessageSender(generateMessageSender());
            ((CC056CType) messageOut).setMessageType(MessageTypes.CC_056_C);
            ((CC056CType) messageOut).setPreparationDateAndTime(nowUTC());
            ((CC056CType) messageOut).setMessageIdentification(MessageUtil.generateMessageIdentification());
            ((CC056CType) messageOut).getTransitOperation().setBusinessRejectionType(messageIn.getClass().getSimpleName().substring(2, 5));
            ((CC056CType) messageOut).getTransitOperation().setRejectionDateAndTime(nowUTC());
        } else if (messageOut instanceof CC057CType) {
            ((CC057CType) messageOut).setMessageSender(generateMessageSender());
            ((CC057CType) messageOut).setMessageType(MessageTypes.CC_057_C);
            ((CC057CType) messageOut).setPreparationDateAndTime(nowUTC());
            ((CC057CType) messageOut).setMessageIdentification(MessageUtil.generateMessageIdentification());
            ((CC057CType) messageOut).getTransitOperation().setBusinessRejectionType(messageIn.getClass().getSimpleName().substring(2, 5));
            ((CC057CType) messageOut).getTransitOperation().setRejectionDateAndTime(nowUTC());
        } else {
            logger.warn("setCoreDataToMessage(): Invalid Message of class: " + messageOut.getClass().getSimpleName());
        }
    }

    private String sanitizeInput(String input) {
        return Objects.nonNull(input) ? input : NOT_AVAILABLE;
    }
}

package com.intrasoft.ermis.transit.cargoofficeofdestination.test.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.intrasoft.ermis.cargov1.messages.CC056CType;
import com.intrasoft.ermis.cargov1.messages.CC057CType;
import com.intrasoft.ermis.cargov1.messages.MessageTypes;
import com.intrasoft.ermis.cargov1.messages.TB007CType;
import com.intrasoft.ermis.cargov1.messages.TB015CType;
import com.intrasoft.ermis.cargov1.messages.TB028CType;
import com.intrasoft.ermis.common.logging.ErmisLogger;
import com.intrasoft.ermis.common.xml.enums.NamespacesEnum;
import com.intrasoft.ermis.common.xml.parsing.XmlConverter;
import com.intrasoft.ermis.transit.cargoofficeofdestination.core.outbound.MessageProducer;
import com.intrasoft.ermis.transit.cargoofficeofdestination.core.service.DossierDataService;
import com.intrasoft.ermis.transit.cargoofficeofdestination.core.service.GenerateMessageServiceImpl;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Declaration;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Message;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.ValidationResult;
import com.intrasoft.ermis.transit.cargoofficeofdestination.test.util.TestDataUtilities;
import com.intrasoft.ermis.transit.common.config.services.GetCountryCodeService;
import com.intrasoft.ermis.transit.common.mappers.CargoMessageTypeMapper;
import com.intrasoft.ermis.transit.common.mappers.CargoMessageTypeMapperImpl;
import com.intrasoft.ermis.transit.common.util.MessageUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import javax.xml.bind.JAXBException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class GenerateMessageServiceUT {

    private static final String MOVEMENT_ID_PREFIX = "MOVEMENT_ID_";

    private static final String MESSAGE_ID_PREFIX = "MESSAGE_ID_";

    private static final String MESSAGE_IDENTIFICATION_PREFIX = "MESSAGE_IDENTIFICATION_";

    private static final String MOCKED_COUNTRY_CODE = "DK";

    private static final String ARRIVAL_PRESENTATION_TIMER_RULE_ID = "CARGO_ARRIVAL_PRESENTATION_TIMER_EXPIRATION";

    private static final String ARRIVAL_PRESENTATION_TIMER_ERROR_CODE = "N/A";

    private static final String ARRIVAL_PRESENTATION_TIMER_ERROR_POINTER = "/TB015C/preparationDateAndTime";

    private static final String GENERIC_RULE_ID = "GENERIC_RULE_ID";

    private static final String GENERIC_ERROR_POINTER = "/TB015C/preparationDateAndTime";

    private static final String GENERIC_ERROR_CODE = "GENERIC_CODE";

    private static final String MRN = "TEST_MRN";

    private static final String TEST_DEPARTURE_OFFICE = "DK000000";

    private static final String TEST_DESTINATION_OFFICE = "DK111111";

    @Mock
    private ErmisLogger logger;

    @Mock
    private DossierDataService dossierDataService;

    @Mock
    private MessageProducer messageProducer;

    @Mock
    private GetCountryCodeService getCountryCodeService;

    @Spy
    private CargoMessageTypeMapper cargoMessageTypeMapper = new CargoMessageTypeMapperImpl();

    @InjectMocks
    private GenerateMessageServiceImpl generateMessageService;

    @Captor
    private ArgumentCaptor<String> messagePayloadCaptor;

    @ParameterizedTest
    @DisplayName("Test - Generate IE056 - Generic cases")
    @MethodSource("provideTestArguments")
    <T> void testGenerateIE056GenericCases(Class<T> messageClass) throws JAXBException {

        String movementId = MOVEMENT_ID_PREFIX + messageClass.getSimpleName();
        String messageId = MESSAGE_ID_PREFIX + messageClass.getSimpleName();
        Message message = createMessage(messageId, messageClass);
        // Stubs/mocks:
        when(dossierDataService.getMessageById(messageId))
                .thenReturn(message);
        when(dossierDataService.findValidationResultListByMessageId(messageId))
                .thenReturn(createValidationResultList(messageId, false));
        when(dossierDataService.saveMessage(any(Message.class)))
                .thenReturn(new Message());
        when(getCountryCodeService.getCountryCode()).thenReturn(MOCKED_COUNTRY_CODE);
        // Call GenerateMessageService method:
        generateMessageService.generateIE056(movementId, messageId, messageClass);
        // Verifications & Assertions:
        verify(dossierDataService).associateMessageWithMovement(any(), eq(movementId));
        verify(messageProducer).publish(messagePayloadCaptor.capture(), eq(movementId), eq(MessageTypes.CC_056_C.value()));
        ie056MessageAssertions(messageClass, messagePayloadCaptor.getValue(), false);
    }

    @Test
    @DisplayName("Test - Generate IE057")
    void testGenerateIE057() throws JAXBException {

        String movementId = MOVEMENT_ID_PREFIX + TB007CType.class.getSimpleName();
        String messageId = MESSAGE_ID_PREFIX + TB007CType.class.getSimpleName();
        Message message = createMessage(messageId, TB007CType.class);
        // Stubs/mocks:
        when(dossierDataService.getMessageById(messageId))
                .thenReturn(message);
        when(dossierDataService.findValidationResultListByMessageId(messageId))
                .thenReturn(createValidationResultList(messageId, false));
        when(dossierDataService.saveMessage(any(Message.class)))
                .thenReturn(new Message());
        when(getCountryCodeService.getCountryCode()).thenReturn(MOCKED_COUNTRY_CODE);
        // Call GenerateMessageService method:
        generateMessageService.generateIE057(movementId, messageId, TB007CType.class);
        // Verifications & Assertions:
        verify(dossierDataService).associateMessageWithMovement(any(), eq(movementId));
        verify(messageProducer).publish(messagePayloadCaptor.capture(), eq(movementId), eq(MessageTypes.CC_057_C.value()));

        CC057CType cc057cTypeMessage = XmlConverter.buildMessageFromXML(messagePayloadCaptor.getValue(), CC057CType.class);
        assertEquals(MessageTypes.CC_057_C, cc057cTypeMessage.getMessageType());
        assertEquals(TB007CType.class.getSimpleName().substring(2, 5), cc057cTypeMessage.getTransitOperation().getBusinessRejectionType());
        assertEquals(CargoMessageTypeMapper.NTA + MOCKED_COUNTRY_CODE, cc057cTypeMessage.getMessageSender());
        assertEquals("12", cc057cTypeMessage.getTransitOperation().getRejectionCode());
        assertNull(cc057cTypeMessage.getTransitOperation().getRejectionReason());
        assertEquals(GENERIC_RULE_ID, cc057cTypeMessage.getFunctionalError().get(0).getErrorReason());
        assertEquals(GENERIC_ERROR_CODE, cc057cTypeMessage.getFunctionalError().get(0).getErrorCode());
        assertEquals(GENERIC_ERROR_POINTER, cc057cTypeMessage.getFunctionalError().get(0).getErrorPointer());
    }

    @Test
    @DisplayName("Test - Generate IE056 - TB015 Arrival Presentation Timer Expiration case")
    void testGenerateIE056TB015ArrivalPresentationTimerExpirationCase() throws JAXBException {

        Class<TB015CType> messageClass = TB015CType.class;
        String movementId = MOVEMENT_ID_PREFIX + messageClass.getSimpleName();
        String messageId = MESSAGE_ID_PREFIX + messageClass.getSimpleName();
        Message message = createMessage(messageId, messageClass);
        // Stubs/mocks:
        when(dossierDataService.getMessageById(messageId))
                .thenReturn(message);
        when(dossierDataService.findValidationResultListByMessageId(messageId))
                .thenReturn(createValidationResultList(messageId, true));
        when(dossierDataService.saveMessage(any(Message.class)))
                .thenReturn(new Message());
        when(getCountryCodeService.getCountryCode()).thenReturn(MOCKED_COUNTRY_CODE);
        // Call GenerateMessageService method:
        generateMessageService.generateIE056(movementId, messageId, messageClass);
        // Verifications & Assertions:
        verify(dossierDataService).associateMessageWithMovement(any(), eq(movementId));
        verify(messageProducer).publish(messagePayloadCaptor.capture(), eq(movementId), eq(MessageTypes.CC_056_C.value()));
        ie056MessageAssertions(messageClass, messagePayloadCaptor.getValue(), true);
    }

    @Test
    @DisplayName("Test - Generate TB028")
    void testGenerateTB028() throws JAXBException {

        String testCaseSuffix = "TB028C";
        String movementId = MOVEMENT_ID_PREFIX + testCaseSuffix;
        TB015CType testTB015CType = TestDataUtilities.createTB015C(TEST_DEPARTURE_OFFICE, TEST_DESTINATION_OFFICE);
        Declaration testDeclaration = TestDataUtilities.createDeclaration(movementId,
                XmlConverter.marshal(testTB015CType, NamespacesEnum.NTA.getValue(), MessageTypes.TB_015_C.value()),
                MessageTypes.TB_015_C.value());
        // Stubs/mocks:
        when(dossierDataService.getLatestDeclarationByMovementId(movementId))
                .thenReturn(testDeclaration);
        when(dossierDataService.saveMessage(any(Message.class)))
                .thenReturn(new Message());
        when(getCountryCodeService.getCountryCode()).thenReturn(MOCKED_COUNTRY_CODE);
        // Call GenerateMessageService method:
        generateMessageService.generateTB028(movementId, MRN);
        // Verifications & Assertions:
        verify(dossierDataService).associateMessageWithMovement(any(), eq(movementId));
        verify(messageProducer).publish(messagePayloadCaptor.capture(), eq(movementId), eq(MessageTypes.TB_028_C.value()));
        TB028CType actualTB028C = XmlConverter.buildMessageFromXML(messagePayloadCaptor.getValue(), TB028CType.class);
        assertEquals(testTB015CType.getTransitOperation().getLRN(), actualTB028C.getTransitOperation().getLRN());
        assertEquals(MRN, actualTB028C.getTransitOperation().getMRN());
        assertEquals(MessageTypes.TB_028_C, actualTB028C.getMessageType());
        assertEquals(TEST_DEPARTURE_OFFICE, actualTB028C.getCustomsOfficeOfDeparture().getReferenceNumber());
        assertEquals(testTB015CType.getMessageSender(), actualTB028C.getMessageRecipient());
        assertEquals(CargoMessageTypeMapper.NTA + MOCKED_COUNTRY_CODE, actualTB028C.getMessageSender());
    }

    private <T> Message createMessage(String messageId, Class<T> messageClass) {

        Message message = new Message();
        message.setId(messageId);
        message.setMessageType(MessageUtil.extractMessageTypeFromMessageClass(messageClass));
        message.setPayload(createMessagePayload(messageClass));

        return message;
    }

    private <T> String createMessagePayload(Class<T> messageClass) {

        if (TB015CType.class.equals(messageClass)) {
            TB015CType messagePayload = TestDataUtilities.createTB015C(TEST_DEPARTURE_OFFICE, TEST_DESTINATION_OFFICE);
            messagePayload.setMessageIdentification(MESSAGE_IDENTIFICATION_PREFIX + messageClass);

            return XmlConverter.marshal(messagePayload, NamespacesEnum.NTA.getValue(), MessageTypes.TB_015_C.value());
        } else if (TB007CType.class.equals(messageClass)) {
            TB007CType messagePayload = TestDataUtilities.createTB007C(TEST_DESTINATION_OFFICE);
            messagePayload.setMessageIdentification(MESSAGE_IDENTIFICATION_PREFIX + messageClass);

            return XmlConverter.marshal(messagePayload, NamespacesEnum.NTA.getValue(), MessageTypes.TB_007_C.value());
        } else {
            return null;
        }
    }

    private List<ValidationResult> createValidationResultList(String messageId, boolean timerExpirationCase) {

        List<ValidationResult> validationResultList = new ArrayList<>();
        ValidationResult validationResult = new ValidationResult();

        if (timerExpirationCase) {
            validationResult.setRuleId(ARRIVAL_PRESENTATION_TIMER_RULE_ID);
            validationResult.setErrorCode(ARRIVAL_PRESENTATION_TIMER_ERROR_CODE);
            validationResult.setPointer(ARRIVAL_PRESENTATION_TIMER_ERROR_POINTER);
        } else {
            validationResult.setRuleId(GENERIC_RULE_ID);
            validationResult.setErrorCode(GENERIC_ERROR_CODE);
            validationResult.setPointer(GENERIC_ERROR_POINTER);
        }
        validationResult.setRejecting(true);
        validationResult.setMessageId(messageId);

        validationResultList.add(validationResult);
        return validationResultList;
    }

    private <T> void ie056MessageAssertions(Class<T> messageClass, String ie056MessagePayload, boolean timerExpirationCase) throws JAXBException {

        CC056CType cc056cTypeMessage = XmlConverter.buildMessageFromXML(ie056MessagePayload, CC056CType.class);

        assertEquals(MessageTypes.CC_056_C, cc056cTypeMessage.getMessageType());
        assertEquals(messageClass.getSimpleName().substring(2, 5), cc056cTypeMessage.getTransitOperation().getBusinessRejectionType());
        assertEquals(CargoMessageTypeMapper.NTA + MOCKED_COUNTRY_CODE, cc056cTypeMessage.getMessageSender());

        if (TB015CType.class.equals(messageClass) && timerExpirationCase) {
            assertEquals("4", cc056cTypeMessage.getTransitOperation().getRejectionCode());
            assertEquals("Arrival Presentation Timer Expiration", cc056cTypeMessage.getTransitOperation().getRejectionReason());
        } else {
            assertEquals("12", cc056cTypeMessage.getTransitOperation().getRejectionCode());
            assertNull(cc056cTypeMessage.getTransitOperation().getRejectionReason());
            assertEquals(GENERIC_RULE_ID, cc056cTypeMessage.getFunctionalError().get(0).getErrorReason());
            assertEquals(GENERIC_ERROR_CODE, cc056cTypeMessage.getFunctionalError().get(0).getErrorCode());
            assertEquals(GENERIC_ERROR_POINTER, cc056cTypeMessage.getFunctionalError().get(0).getErrorPointer());
        }
    }

    private static Stream<Arguments> provideTestArguments() {
        return Stream.of(
                Arguments.of(TB015CType.class)
        );
    }
}
