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

import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.DeclarationDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.MessageDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.MessageOverviewDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.MovementDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.ProcessingStatusDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.ValidationResultDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.core.service.DossierDataService;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Declaration;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Message;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.MessageOverview;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Movement;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.ProcessingStatus;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.ValidationResult;
import com.intrasoft.ermis.transit.common.exceptions.NotFoundException;
import com.intrasoft.ermis.platform.contracts.dossier.dtos.MessageOverviewDTO;
import com.intrasoft.ermis.platform.contracts.dossier.dtos.PaginatedDTO;
import com.intrasoft.ermis.transit.shared.dossier.clients.DossierDeclarationPort;
import com.intrasoft.ermis.transit.shared.dossier.clients.DossierMessagePort;
import com.intrasoft.ermis.transit.shared.dossier.clients.DossierMovementPort;
import com.intrasoft.ermis.transit.shared.dossier.clients.DossierProcessingStatusPort;
import com.intrasoft.ermis.transit.shared.dossier.clients.DossierValidationResultsPort;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class DossierDataServiceImpl implements DossierDataService {
    // Ports:
    private final DossierMovementPort dossierMovementPort;

    private final DossierMessagePort dossierMessagePort;

    private final DossierProcessingStatusPort dossierProcessingStatusPort;

    private final DossierDeclarationPort dossierDeclarationPort;

    private final DossierValidationResultsPort dossierValidationResultsPort;

    // Mappers:
    private final MovementDTOMapper movementDTOMapper;

    private final MessageDTOMapper messageDTOMapper;

    private final MessageOverviewDTOMapper messageOverviewDTOMapper;

    private final ProcessingStatusDTOMapper processingStatusDTOMapper;

    private final DeclarationDTOMapper declarationDTOMapper;

    private final ValidationResultDTOMapper validationResultDTOMapper;

    @Override
    public Movement getMovementById(String movementId) {
        return movementDTOMapper.toDomain(dossierMovementPort.findById(movementId, false).getBody());
    }

    @Override
    public Movement updateMovement(Movement movement) {
        return movementDTOMapper.toDomain(dossierMovementPort.update(movementDTOMapper.toDTO(movement), movement.getId()).getBody());
    }

    @Override
    public List<MessageOverview> getMessageOverviewList(String movementId, String messageType, Boolean isRejected) {

        PaginatedDTO<MessageOverviewDTO> messageOverviewDTOListBody =
                dossierMessagePort.findOverviewByCriteria(movementId, messageType, null, isRejected, null, null, 0, 20, false).getBody();

        if (messageOverviewDTOListBody != null) {

            return messageOverviewDTOMapper.toDomain(messageOverviewDTOListBody.getContent());
        } else {

            log.error("Message Overview list body was NULL, returning empty list.");
            return new ArrayList<>();
        }
    }

    @Override
    public Message saveMessage(Message message) {
        return messageDTOMapper.toDomain(dossierMessagePort.add(messageDTOMapper.toDTO(message)).getBody());
    }

    @Override
    public Message getMessageById(String messageId) {
        return messageDTOMapper.toDomain(dossierMessagePort.findById(messageId, false, false, false).getBody());
    }

    @Override
    public void associateMessageWithMovement(String messageId, String movementId) {
        dossierMessagePort.associateMovement(messageId, movementId);
    }

    @Override
    public Message updateMessageStatus(String id, String status) {
        return messageDTOMapper.toDomain(dossierMessagePort.update(id, status).getBody());
    }

    @Override
    public void saveProcessingStatus(ProcessingStatus processingStatus) {
        dossierProcessingStatusPort.add(processingStatusDTOMapper.toDTO(processingStatus));
    }

    @Override
    public void createDeclaration(Declaration declaration) {
        dossierDeclarationPort.add(declarationDTOMapper.toDTO(declaration));
    }

    @Override
    public Declaration getLatestDeclarationByMovementId(String movementId) {
        try {
            return declarationDTOMapper.toDomain(dossierDeclarationPort.findCurrentVersionByMovementId(movementId, false, false).getBody());
        } catch (NotFoundException e) {
            return null;
        }
    }

    @Override
    public void saveValidationResultList(List<ValidationResult> validationResultList) {
        dossierValidationResultsPort.saveValidationResults(validationResultDTOMapper.toDTO(validationResultList));
    }

    @Override
    public List<ValidationResult> findValidationResultListByMessageId(String messageId) {
        return validationResultDTOMapper.toDomain(dossierValidationResultsPort.findValidationResultsByMessageId(messageId).getBody());
    }

    @Override
    public boolean mrnExists(String mrn) {
        return Boolean.TRUE.equals(dossierMovementPort.existsByCriteria(mrn, null, null, null).getBody());
    }
}

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

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

import com.intrasoft.ermis.cargov1.messages.MessageTypes;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.DeclarationDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.DeclarationDTOMapperImpl;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.MessageDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.MessageDTOMapperImpl;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.MessageOverviewDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.MessageOverviewDTOMapperImpl;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.MovementDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.MovementDTOMapperImpl;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.ProcessingStatusDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.ProcessingStatusDTOMapperImpl;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.ValidationResultDTOMapper;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.mapper.ValidationResultDTOMapperImpl;
import com.intrasoft.ermis.transit.cargoofficeofdestination.adapter.service.DossierDataServiceImpl;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Declaration;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Message;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Movement;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.ProcessingStatus;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.ValidationResult;
import com.intrasoft.ermis.transit.common.enums.MessageStatusEnum;
import com.intrasoft.ermis.platform.contracts.dossier.dtos.DeclarationDTO;
import com.intrasoft.ermis.platform.contracts.dossier.dtos.MessageDTO;
import com.intrasoft.ermis.platform.contracts.dossier.dtos.MovementDTO;
import com.intrasoft.ermis.platform.contracts.dossier.dtos.PaginatedDTO;
import com.intrasoft.ermis.platform.contracts.dossier.dtos.ProcessingStatusDTO;
import com.intrasoft.ermis.platform.contracts.dossier.dtos.ValidationResultDTO;
import com.intrasoft.ermis.transit.shared.dossier.clients.DossierDeclarationPort;
import com.intrasoft.ermis.transit.shared.dossier.clients.DossierMessagePort;
import com.intrasoft.ermis.transit.shared.dossier.clients.DossierMovementPort;
import com.intrasoft.ermis.transit.shared.dossier.clients.DossierProcessingStatusPort;
import com.intrasoft.ermis.transit.shared.dossier.clients.DossierValidationResultsPort;
import java.util.ArrayList;
import java.util.List;
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.ValueSource;
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;
import org.springframework.http.ResponseEntity;

@ExtendWith(MockitoExtension.class)
class DossierDataServiceUT {

    private static final String MOVEMENT_ID = "TEST_MOVEMENT_ID";

    private static final String MESSAGE_ID = "TEST_MESSAGE_ID";

    private static final String MESSAGE_IDENTIFICATION = "TEST_MESSAGE_IDENTIFICATION";

    private static final String MRN = "TEST_MRN";

    @Mock
    private DossierMovementPort dossierMovementPort;

    @Mock
    private DossierMessagePort dossierMessagePort;

    @Mock
    private DossierProcessingStatusPort dossierProcessingStatusPort;

    @Mock
    private DossierDeclarationPort dossierDeclarationPort;

    @Mock
    private DossierValidationResultsPort dossierValidationResultsPort;

    @Spy
    private MovementDTOMapper movementDTOMapper = new MovementDTOMapperImpl();

    @Spy
    private MessageDTOMapper messageDTOMapper = new MessageDTOMapperImpl();

    @Spy
    private MessageOverviewDTOMapper messageOverviewDTOMapper = new MessageOverviewDTOMapperImpl();

    @Spy
    private ProcessingStatusDTOMapper processingStatusDTOMapper = new ProcessingStatusDTOMapperImpl();

    @Spy
    private DeclarationDTOMapper declarationDTOMapper = new DeclarationDTOMapperImpl();

    @Spy
    private ValidationResultDTOMapper validationResultDTOMapper = new ValidationResultDTOMapperImpl();

    @InjectMocks
    private DossierDataServiceImpl dossierDataService;

    @Captor
    private ArgumentCaptor<MovementDTO> movementDTOArgumentCaptor;

    @Captor
    private ArgumentCaptor<ProcessingStatusDTO> processingStatusDTOArgumentCaptor;

    @Captor
    private ArgumentCaptor<DeclarationDTO> declarationDTOArgumentCaptor;

    @Captor
    private ArgumentCaptor<MessageDTO> messageDTOArgumentCaptor;

    @Captor
    private ArgumentCaptor<List<ValidationResultDTO>> validationResultDTOListArgumentCaptor;

    @Captor
    private ArgumentCaptor<String> messageIdCaptor;

    @Captor
    private ArgumentCaptor<String> statusCaptor;

    @Test
    @DisplayName("Test - Get Movement by ID")
    void testGetMovementById() {
        // Stub:
        when(dossierMovementPort.findById(MOVEMENT_ID, false)).thenReturn(ResponseEntity.ok(new MovementDTO()));
        // Call DossierDataService method:
        dossierDataService.getMovementById(MOVEMENT_ID);
        // Verification:
        verify(dossierMovementPort, times(1)).findById(MOVEMENT_ID, false);
    }

    @Test
    @DisplayName("Test - Update Movement")
    void testUpdateMovement() {
        Movement testMovement = Movement.builder().id(MOVEMENT_ID).build();
        // Stub:
        when(dossierMovementPort.update(any(), eq(MOVEMENT_ID))).thenReturn(ResponseEntity.ok(new MovementDTO()));
        // Call DossierDataService method:
        dossierDataService.updateMovement(testMovement);
        // Verification & Assertion:
        verify(dossierMovementPort, times(1)).update(movementDTOArgumentCaptor.capture(), eq(MOVEMENT_ID));
        assertEquals(MOVEMENT_ID, movementDTOArgumentCaptor.getValue().getId());
    }

    @Test
    @DisplayName("Test - Get Message Overview List")
    void testGetMessageOverviewList() {
        // Stub:
        when(dossierMessagePort.findOverviewByCriteria(MOVEMENT_ID, MessageTypes.TB_015_C.value(), null,
                null, null, null, 0, 20, false)).thenReturn(ResponseEntity.ok(new PaginatedDTO<>()));
        // Call DossierDataService method:
        dossierDataService.getMessageOverviewList(MOVEMENT_ID, MessageTypes.TB_015_C.value(), null);
        // Verification:
        verify(dossierMessagePort, times(1)).findOverviewByCriteria(MOVEMENT_ID, MessageTypes.TB_015_C.value(), null,
                null, null, null, 0, 20, false);
    }

    @Test
    @DisplayName("Test - Save Message")
    void testSaveMessage() {
        Message testMessage = Message.builder().messageIdentification(MESSAGE_IDENTIFICATION).build();
        // Stub:
        when(dossierMessagePort.add(any(MessageDTO.class))).thenReturn(ResponseEntity.ok(new MessageDTO()));
        // Call DossierDataService method:
        dossierDataService.saveMessage(testMessage);
        // Verification & Assertion:
        verify(dossierMessagePort, times(1)).add(messageDTOArgumentCaptor.capture());
        assertEquals(MESSAGE_IDENTIFICATION, messageDTOArgumentCaptor.getValue().getMessageIdentification());
    }

    @Test
    @DisplayName("Test - Get Message by ID")
    void testGetMessageById() {
        // Stub:
        when(dossierMessagePort.findById(MESSAGE_ID, false, false, false)).thenReturn(ResponseEntity.ok(new MessageDTO()));
        // Call DossierDataService method:
        dossierDataService.getMessageById(MESSAGE_ID);
        // Verification:
        verify(dossierMessagePort, times(1)).findById(MESSAGE_ID, false, false, false);
    }

    @Test
    @DisplayName("Test - Associate Message with Movement")
    void testAssociateMessageWithMovement() {
        // Stub:
        when(dossierMessagePort.associateMovement(MESSAGE_ID, MOVEMENT_ID)).thenReturn(ResponseEntity.ok(null));
        // Call DossierDataService method:
        dossierDataService.associateMessageWithMovement(MESSAGE_ID, MOVEMENT_ID);
        // Verification:
        verify(dossierMessagePort, times(1)).associateMovement(MESSAGE_ID, MOVEMENT_ID);
    }

    @Test
    @DisplayName("Test - Update Message Status")
    void testUpdateMessageStatus() {
        // Stub:
        when(dossierMessagePort.update(MESSAGE_ID, MessageStatusEnum.REJECTED.name())).thenReturn(ResponseEntity.ok(new MessageDTO()));
        // Call DossierDataService method:
        dossierDataService.updateMessageStatus(MESSAGE_ID, MessageStatusEnum.REJECTED.name());
        // Verification:
        verify(dossierMessagePort, times(1)).update(MESSAGE_ID, MessageStatusEnum.REJECTED.name());
    }

    @Test
    @DisplayName("Test - Save Processing Status")
    void testSaveProcessingStatus() {

        ProcessingStatus testProcessingStatus = ProcessingStatus.builder()
                                                                .movementId(MOVEMENT_ID)
                                                                .status("TEST_STATUS")
                                                                .reasonType("TEST_REASON")
                                                                .build();

        // Call DossierDataService method:
        dossierDataService.saveProcessingStatus(testProcessingStatus);
        // Verification & Assertions:
        verify(dossierProcessingStatusPort, times(1)).add(processingStatusDTOArgumentCaptor.capture());
        assertEquals(testProcessingStatus.getMovementId(), processingStatusDTOArgumentCaptor.getValue().getMovementId());
        assertEquals(testProcessingStatus.getStatus(), processingStatusDTOArgumentCaptor.getValue().getStatus());
        assertEquals(testProcessingStatus.getReasonType(), processingStatusDTOArgumentCaptor.getValue().getReasonType());
    }

    @Test
    @DisplayName("Test - Create Declaration")
    void testCreateDeclaration() {
        Declaration testMovement = Declaration.builder().movementId(MOVEMENT_ID).build();
        // Call DossierDataService method:
        dossierDataService.createDeclaration(testMovement);
        // Verification & Assertion:
        verify(dossierDeclarationPort, times(1)).add(declarationDTOArgumentCaptor.capture());
        assertEquals(MOVEMENT_ID, declarationDTOArgumentCaptor.getValue().getMovementId());
    }

    @Test
    @DisplayName("Test - Get latest Declaration by Movement ID")
    void testGetLatestDeclarationByMovementId() {
        // Stub:
        when(dossierDeclarationPort.findCurrentVersionByMovementId(MOVEMENT_ID, false, false)).thenReturn(ResponseEntity.ok(new DeclarationDTO()));
        // Call DossierDataService method:
        dossierDataService.getLatestDeclarationByMovementId(MOVEMENT_ID);
        // Verification:
        verify(dossierDeclarationPort, times(1)).findCurrentVersionByMovementId(MOVEMENT_ID, false, false);
    }

    @Test
    @DisplayName("Test - Save Validation Result List")
    void testSaveValidationResultList() {
        List<ValidationResult> testValidationResultList = new ArrayList<>();
        ValidationResult testValidationResult = new ValidationResult();
        testValidationResult.setMessageId(MESSAGE_ID);
        testValidationResultList.add(testValidationResult);
        // Call DossierDataService method:
        dossierDataService.saveValidationResultList(testValidationResultList);
        // Verification & Assertion:
        verify(dossierValidationResultsPort, times(1)).saveValidationResults(validationResultDTOListArgumentCaptor.capture());
        assertEquals(MESSAGE_ID, validationResultDTOListArgumentCaptor.getValue().get(0).getMessageId());
    }

    @Test
    @DisplayName("Test - Find Validation Result list by Message ID")
    void testFindValidationResultListByMessageId() {
        // Stub:
        when(dossierValidationResultsPort.findValidationResultsByMessageId(MESSAGE_ID)).thenReturn(ResponseEntity.ok(new ArrayList<>()));
        // Call DossierDataService method:
        dossierDataService.findValidationResultListByMessageId(MESSAGE_ID);
        // Verification:
        verify(dossierValidationResultsPort, times(1)).findValidationResultsByMessageId(MESSAGE_ID);
    }

    @ParameterizedTest
    @DisplayName("Test - MRN Exists")
    @ValueSource(booleans = {true, false})
    void testMRNExists(boolean mrnExists) {
        // Stub:
        when(dossierMovementPort.existsByCriteria(MRN, null, null, null))
                .thenReturn(ResponseEntity.ok(mrnExists));
        // Call DossierDataService method:
        boolean returnedValue = dossierDataService.mrnExists(MRN);
        // Verification & Assertion:
        verify(dossierMovementPort, times(1)).existsByCriteria(MRN, null, null, null);
        assertEquals(mrnExists, returnedValue);
    }
}
