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

import com.intrasoft.ermis.common.json.parsing.JsonConverter;
import com.intrasoft.ermis.common.json.parsing.JsonConverterFactory;
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.util.MessageUtil;
import com.intrasoft.ermis.transit.contracts.management.dto.action.data.StatusRequestEvent;
import com.intrasoft.ermis.transit.management.core.port.outbound.DossierDataService;
import com.intrasoft.ermis.transit.management.core.port.outbound.StatusRequestProducer;
import com.intrasoft.ermis.transit.management.domain.Message;
import com.intrasoft.ermis.transit.shared.security.service.UserDetailsService;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class StatusRequestServiceImpl implements StatusRequestService {

    private static final JsonConverter JSON_CONVERTER = JsonConverterFactory.getDefaultJsonConverter();

    private final StatusRequestProducer statusRequestProducer;

    private final UserDetailsService userDetailsService;

    private final GetCountryCodeService getCountryCodeService;

    private final DossierDataService dossierDataService;

    // TODO: Should be refactored to use action system
    @Override
    public void handleStatusRequest(StatusRequestEvent statusRequestEvent) {

        statusRequestEvent.setRequestId(UUID.randomUUID().toString());

        Message message = Message.builder()
                                 .messageIdentification(MessageUtil.generateMessageIdentification())
                                 .messageType(StatusRequestEvent.class.getSimpleName())
                                 .source(userDetailsService.getUsername())
                                 .destination("NTA." + getCountryCodeService.getCountryCode())
                                 .payload(JSON_CONVERTER.convertToJsonString(statusRequestEvent))
                                 .domain(MessageDomainTypeEnum.ERMIS.name())
                                 .direction(DirectionEnum.RECEIVED.getDirection())
                                 .build();

        message = dossierDataService.persistMessage(message);

        dossierDataService.associateMessageWithMovement(message.getId(), statusRequestEvent.getMovementId());

        statusRequestProducer.publish(statusRequestEvent);
    }
}

package com.intrasoft.ermis.transit.officeofexitfortransit.service;

import static com.intrasoft.ermis.transit.officeofexitfortransit.util.DataGenerators.createIE094Message;
import static com.intrasoft.ermis.transit.officeofexitfortransit.util.DataGenerators.createIE168Message;
import static com.intrasoft.ermis.transit.officeofexitfortransit.util.DataGenerators.generateRandomMRN;
import static com.intrasoft.ermis.transit.officeofexitfortransit.util.Stubs.stubSaveMessage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.intrasoft.ermis.common.xml.parsing.XmlConverter;
import com.intrasoft.ermis.ddntav5152.messages.CD094CType;
import com.intrasoft.ermis.ddntav5152.messages.CD168CType;
import com.intrasoft.ermis.ddntav5152.messages.MessageTypes;
import com.intrasoft.ermis.transit.common.enums.OfficeRoleTypesEnum;
import com.intrasoft.ermis.transit.common.enums.ProcessingStatusTypeEnum;
import com.intrasoft.ermis.transit.officeofexitfortransit.core.outbound.DDNTAMessageProducer;
import com.intrasoft.ermis.transit.officeofexitfortransit.core.service.DossierDataService;
import com.intrasoft.ermis.transit.officeofexitfortransit.core.service.GenerateMessageService;
import com.intrasoft.ermis.transit.officeofexitfortransit.core.service.StatusRequestServiceImpl;
import com.intrasoft.ermis.transit.officeofexitfortransit.domain.Message;
import com.intrasoft.ermis.transit.officeofexitfortransit.domain.MessageOverview;
import com.intrasoft.ermis.transit.officeofexitfortransit.domain.Movement;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class StatusRequestServiceUT {

    private final String MOVEMENT_ID = UUID.randomUUID().toString();

    private final String MRN = generateRandomMRN();

    private static final String IE094_MESSAGE_ID = "IE094_MESSAGE_ID";

    private static final String IE168_MESSAGE_ID = "IE168_MESSAGE_ID";

    @Mock
    private DossierDataService dossierDataService;

    @Mock
    private GenerateMessageService generateMessageService;

    @Mock
    private DDNTAMessageProducer ddntaMessageProducer;

    @InjectMocks
    private StatusRequestServiceImpl statusReportRequestService;

    @Captor
    private ArgumentCaptor<String> xmlArgumentCaptor;

    @Test
    @DisplayName("Test - Handle IE094 Status Report Request")
    void testHandleIE094StatusRequest() {

        System.setProperty("com.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize", "true");

        // Stubs:
        Movement movement = createMovement(MOVEMENT_ID);
        CD094CType cd094cTypeMessage = createIE094Message(MRN);
        CD168CType cd168cTypeMessage = createIE168Message(MRN);
        List<MessageOverview> messageOverviewList = createMessageOverviewList();

        try {
            // Mocks:
            when(dossierDataService.getMovementByCriteria(MRN, OfficeRoleTypesEnum.EXIT.getValue(), "IE094_EXIT_OFFICE_REF_NUM")).thenReturn(movement);
            doNothing().when(generateMessageService).generateIE095(eq(MOVEMENT_ID), any(), any());
            when(dossierDataService.getMessageById(IE094_MESSAGE_ID))
                    .thenReturn(getMessage(MessageTypes.CD_094_C.value(), XmlConverter.marshal(cd094cTypeMessage)));
            when(dossierDataService.getMessageById(IE168_MESSAGE_ID))
                    .thenReturn(getMessage(MessageTypes.CD_168_C.value(), XmlConverter.marshal(cd168cTypeMessage)));
            when(dossierDataService.getMessagesOverview(MOVEMENT_ID, MessageTypes.CD_168_C.value())).thenReturn(messageOverviewList);
            when(dossierDataService.saveMessage(any())).thenAnswer(stubSaveMessage());
            doNothing().when(dossierDataService).associateMessageWithMovement(any(), eq(MOVEMENT_ID));
            // Call StatusRequestService method:
            statusReportRequestService.handleIE094StatusRequest(MRN, IE094_MESSAGE_ID, "IE094_EXIT_OFFICE_REF_NUM");

            // Verifications & Assertions:
            verify(ddntaMessageProducer, times(1)).publish(xmlArgumentCaptor.capture(), eq(MOVEMENT_ID), eq(MessageTypes.CD_168_C.value()));
            CD168CType cd168cTypeMessageOut = XmlConverter.buildMessageFromXML(xmlArgumentCaptor.getValue(), CD168CType.class);
            verify(dossierDataService, times(1)).getMovementByCriteria(MRN, OfficeRoleTypesEnum.EXIT.getValue(), "IE094_EXIT_OFFICE_REF_NUM");
            verify(dossierDataService, times(1)).getMessageById(IE094_MESSAGE_ID);
            verify(dossierDataService, times(1)).getMessageById(IE168_MESSAGE_ID);
            verify(dossierDataService, times(1)).getMessagesOverview(MOVEMENT_ID, MessageTypes.CD_168_C.value());
            verify(dossierDataService, times(1)).saveMessage(any());
            verify(dossierDataService, times(1)).associateMessageWithMovement(any(), eq(MOVEMENT_ID));

            assertEquals(cd168cTypeMessage.getMessageSender(), cd168cTypeMessageOut.getMessageSender());
            assertEquals(cd168cTypeMessage.getMessageRecipient(), cd168cTypeMessageOut.getMessageRecipient());
            assertEquals(cd168cTypeMessage.getTransitOperation().getMRN(), cd168cTypeMessageOut.getTransitOperation().getMRN());
            assertNotEquals(cd168cTypeMessage.getMessageIdentification(), cd168cTypeMessageOut.getMessageIdentification());
            assertNotEquals(cd168cTypeMessage.getPreparationDateAndTime(), cd168cTypeMessageOut.getPreparationDateAndTime());
        } catch (Exception e) {

            fail();
        }
    }

    private Movement createMovement(String movementId) {

        return Movement.builder()
                       .id(movementId)
                       .mrn(MRN)
                       .processingStatus(ProcessingStatusTypeEnum.OOTRXS_MOVEMENT_LEFT_EUROPEAN_CUSTOMS_SECURITY_AREA.getItemCode())
                       .officeRoleType(OfficeRoleTypesEnum.EXIT.getValue())
                       .officeId("DK_TEST_OFFICE_ID")
                       .build();
    }

    private List<MessageOverview> createMessageOverviewList() {

        List<MessageOverview> messageOverviewList = new ArrayList<>();

        MessageOverview ie168MessageOverview = new MessageOverview();
        ie168MessageOverview.setMessageType(MessageTypes.CD_168_C.value());
        ie168MessageOverview.setId(IE168_MESSAGE_ID);

        messageOverviewList.add(ie168MessageOverview);

        return messageOverviewList;
    }

    private Message getMessage(String messageType, String payload) {

        return Message.builder()
                      .id(UUID.randomUUID().toString())
                      .messageIdentification(UUID.randomUUID().toString())
                      .messageType(messageType)
                      .source("DEPARTURE")
                      .destination("EXIT")
                      .domain("COMMON")
                      .createdDateTime(LocalDateTime.now(ZoneOffset.UTC))
                      .payload(payload)
                      .build();
    }
}
