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

import com.intrasoft.ermis.common.logging.ErmisLogger;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Movement;
import com.intrasoft.ermis.transit.common.config.services.GetCountryCodeService;
import com.intrasoft.ermis.transit.common.config.services.MRNFormulaConfigurationService;
import com.intrasoft.ermis.transit.common.config.services.MRNGenerationService;
import com.intrasoft.ermis.transit.common.exceptions.BaseException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class MRNAllocationServiceImpl implements MRNAllocationService {

    private final ErmisLogger logger;

    private final DossierDataService dossierDataService;

    private final GetCountryCodeService getCountryCodeService;

    private final MRNFormulaConfigurationService mrnFormulaConfigurationService;

    private final MRNGenerationService mrnGenerationService;

    @Override
    public String allocateMRN(String movementId) {

        Movement movement;
        String mrn;
        String countryCode = getCountryCodeService.getCountryCode();

        try {
            movement = dossierDataService.getMovementById(movementId);

            String expression = mrnFormulaConfigurationService.retrieveTransitMrnFormula();
            mrn = mrnGenerationService.generateMrnJexl(movement.getCreatedDateTime(), movement.getOfficeOfDeparture(), "", countryCode, expression);
            assignMrn(movement, countryCode, mrn, expression);
            movement.setAcceptanceDateTime(LocalDateTime.now(ZoneOffset.UTC));

            dossierDataService.updateMovement(movement);
        } catch (Exception e) {
            throw new BaseException("Error retrieving movement with ID: " + movementId, e);
        }
        logger.info("Allocated MRN: " + movement.getMrn() + " and acceptanceDateTime: " + movement.getAcceptanceDateTime() + " on Movement with ID: " + movementId);
        return movement.getMrn();
    }

    private void assignMrn(Movement movement, String countryCode, String mrn, String expression) {

        if (dossierDataService.mrnExists(mrn)) {
            logger.info("The MRN that was created exists, a new one will be created.");
            String newMRN =
                    mrnGenerationService.generateMrnJexl(movement.getCreatedDateTime(), movement.getOfficeOfDeparture(), movement.getSecurity(), countryCode,
                            expression);
            assignMrn(movement, countryCode, newMRN, expression);
        } else {
            movement.setMrn(mrn);
        }
    }
}

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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
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.common.logging.ErmisLogger;
import com.intrasoft.ermis.transit.cargoofficeofdestination.core.service.DossierDataService;
import com.intrasoft.ermis.transit.cargoofficeofdestination.core.service.MRNAllocationServiceImpl;
import com.intrasoft.ermis.transit.cargoofficeofdestination.domain.Movement;
import com.intrasoft.ermis.transit.common.config.services.GetCountryCodeService;
import com.intrasoft.ermis.transit.common.config.services.MRNFormulaConfigurationService;
import com.intrasoft.ermis.transit.common.config.services.MRNGenerationService;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
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 MRNAllocationServiceUT {

    private static final String MOVEMENT_ID = "MOCK_MOVEMENT_ID";

    private static final String OFFICE_OF_DEPARTURE_ID = "MOCK_OFFICE_OF_DEPARTURE_ID";

    private static final String COUNTRY_CODE = "MOCK_COUNTRY";

    private static final String FORMULA = "MOCK_FORMULA";

    private static final String MRN = "MOCK_MRN";

    @Mock
    private ErmisLogger logger;

    @Mock
    private DossierDataService dossierDataService;

    @Mock
    private GetCountryCodeService getCountryCodeService;

    @Mock
    private MRNFormulaConfigurationService mrnFormulaConfigurationService;

    @Mock
    private MRNGenerationService mrnGenerationService;

    @InjectMocks
    private MRNAllocationServiceImpl mrnAllocationService;

    @Captor
    private ArgumentCaptor<Movement> movementArgumentCaptor;

    @Test
    @DisplayName("Test - Allocate MRN")
    void testAllocateMRN() {
        // Test data:
        Movement mockMovement = Movement.builder()
                                        .id(MOVEMENT_ID)
                                        .createdDateTime(LocalDateTime.now(ZoneOffset.UTC))
                                        .officeOfDeparture(OFFICE_OF_DEPARTURE_ID)
                                        .build();
        // Stubs:
        when(getCountryCodeService.getCountryCode()).thenReturn(COUNTRY_CODE);
        when(dossierDataService.getMovementById(MOVEMENT_ID)).thenReturn(mockMovement);
        when(mrnFormulaConfigurationService.retrieveTransitMrnFormula()).thenReturn(FORMULA);
        when(mrnGenerationService.generateMrnJexl(any(LocalDateTime.class), eq(OFFICE_OF_DEPARTURE_ID), eq(""), eq(COUNTRY_CODE), eq(FORMULA)))
                .thenReturn(MRN);
        when(dossierDataService.mrnExists(MRN)).thenReturn(false);
        // Call tested service method:
        mrnAllocationService.allocateMRN(MOVEMENT_ID);
        // Verifications & Assertions:
        verify(mrnGenerationService, times(1)).generateMrnJexl(any(LocalDateTime.class), any(), any(), any(), any());
        verify(dossierDataService, times(1)).updateMovement(movementArgumentCaptor.capture());
        assertEquals(MRN, movementArgumentCaptor.getValue().getMrn());
        assertNotNull(movementArgumentCaptor.getValue().getAcceptanceDateTime());
    }
}
