package com.iqser.red.service.peristence.v1.server.integration.tests;

import static com.iqser.red.service.persistence.management.v1.processor.utils.TypeIdUtils.toTypeId;
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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.commons.jackson.ObjectMapperFactory;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.SystemManagedTypesImport;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.migration.RankDeDuplicationService;
import com.iqser.red.service.persistence.management.v1.processor.service.ColorsService;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService;
import com.iqser.red.service.persistence.management.v1.processor.service.CurrentApplicationTypeProvider;
import com.iqser.red.service.persistence.management.v1.processor.service.DictionaryManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateCloneService;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.DossierTemplateImportService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.LayoutParsingTypeProvider;
import com.iqser.red.service.persistence.management.v1.processor.service.WatermarkService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ComponentDefinitionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.DossierTemplateExportService;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.EntityTypeExportService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DateFormatsPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.EntryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ReportTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierTemplateRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.TypeRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.CloneDossierTemplateRequest;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.ExportDownloadRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.TypeRankSummary;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType;

import lombok.SneakyThrows;

@ExtendWith(SpringExtension.class)
public class DossierTemplateCloneAndExportWithDuplicateRanksTest {

    @MockBean
    private DossierTemplateRepository dossierTemplateRepository;
    @MockBean
    private LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;
    @MockBean
    private RulesPersistenceService rulesPersistenceService;
    @MockBean
    private DateFormatsPersistenceService dateFormatsPersistenceService;
    @MockBean
    private DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService;
    @MockBean
    private DictionaryPersistenceService dictionaryPersistenceService;
    @MockBean
    private EntryPersistenceService entryPersistenceService;
    @MockBean
    private FileAttributeConfigPersistenceService fileAttributeConfigPersistenceService;
    @MockBean
    private ReportTemplatePersistenceService reportTemplatePersistenceService;
    @MockBean
    private ColorsService colorsService;
    @MockBean
    private ComponentMappingService componentMappingService;
    @MockBean
    private StorageService storageService;
    @MockBean
    private DossierStatusPersistenceService dossierStatusPersistenceService;
    @MockBean
    private WatermarkService watermarkService;
    @MockBean
    private FileManagementStorageService fileManagementStorageService;
    @MockBean
    private DownloadStatusPersistenceService downloadStatusPersistenceService;
    @MockBean
    private RabbitTemplate rabbitTemplate;
    @MockBean
    private TypeRepository typeRepository;

    @MockBean
    private DossierTemplateImportService dossierTemplateImportService;
    @MockBean
    private ComponentDefinitionPersistenceService componentDefinitionPersistenceService;

    private DossierTemplatePersistenceService dossierTemplatePersistenceService;
    private DossierTemplateCloneService dossierTemplateCloneService;
    private DossierTemplateExportService dossierTemplateExportService;
    private EntityTypeExportService entityTypeExportService;
    private RankDeDuplicationService rankDeDuplicationService;
    @MockBean
    private DictionaryManagementService dictionaryManagementService;
    @MockBean
    private SystemManagedTypesImport systemManagedTypesImport;

    @MockBean
    private CurrentApplicationTypeProvider currentApplicationTypeProvider;
    @MockBean
    private LayoutParsingTypeProvider layoutParsingTypeProvider;

    ObjectMapper mapper;

    private DossierTemplateManagementService dossierTemplateManagementService;
    private String dossierTemplateId = "dossierTemplateId";
    private String dossierId = "dossierId";
    private String dossierTemplateName = "Clone of " + dossierTemplateId;

    @Captor
    private ArgumentCaptor<Integer> captor;


    @BeforeEach
    public void setupData() {

        mapper = ObjectMapperFactory.create();

        dossierTemplatePersistenceService = new DossierTemplatePersistenceService(dossierTemplateRepository,
                                                                                  legalBasisMappingPersistenceService,
                                                                                  rulesPersistenceService,
                                                                                  dictionaryPersistenceService,
                                                                                  currentApplicationTypeProvider,
                                                                                  layoutParsingTypeProvider,
                                                                                  typeRepository);
        dossierTemplateCloneService = new DossierTemplateCloneService(dossierTemplateRepository,
                                                                      legalBasisMappingPersistenceService,
                                                                      rulesPersistenceService,
                                                                      dateFormatsPersistenceService,
                                                                      dossierTemplatePersistenceService,
                                                                      dossierAttributeConfigPersistenceService,
                                                                      dictionaryPersistenceService,
                                                                      entryPersistenceService,
                                                                      fileAttributeConfigPersistenceService,
                                                                      reportTemplatePersistenceService,
                                                                      colorsService,
                                                                      storageService,
                                                                      dossierStatusPersistenceService,
                                                                      watermarkService,
                                                                      fileManagementStorageService,
                                                                      componentMappingService,
                                                                      componentDefinitionPersistenceService);

        entityTypeExportService = new EntityTypeExportService(mapper, entryPersistenceService);

        dossierTemplateExportService = new DossierTemplateExportService(dossierTemplatePersistenceService,
                                                                        downloadStatusPersistenceService,
                                                                        dossierAttributeConfigPersistenceService,
                                                                        dossierStatusPersistenceService,
                                                                        dictionaryPersistenceService,
                                                                        fileAttributeConfigPersistenceService,
                                                                        legalBasisMappingPersistenceService,
                                                                        rulesPersistenceService,
                                                                        dateFormatsPersistenceService,
                                                                        fileManagementStorageService,
                                                                        reportTemplatePersistenceService,
                                                                        componentMappingService,
                                                                        colorsService,
                                                                        mapper,
                                                                        entityTypeExportService,
                                                                        componentDefinitionPersistenceService,
                                                                        watermarkService,
                                                                        rabbitTemplate);
        dossierTemplateManagementService = new DossierTemplateManagementService(dossierTemplateExportService,
                                                                                dossierTemplateImportService,
                                                                                dossierTemplatePersistenceService,
                                                                                dossierTemplateCloneService,
                                                                                systemManagedTypesImport,
                                                                                dictionaryManagementService);

        rankDeDuplicationService = new RankDeDuplicationService(dossierTemplateManagementService, dictionaryPersistenceService);

        List<TypeRankSummary> typesValues = new ArrayList<>();
        TypeRankSummary typeRank1 = new TypeRankSummary(dossierTemplateId, null, 50, 2);
        TypeRankSummary typeRank2 = new TypeRankSummary(dossierTemplateId, null, 100, 1);
        typesValues.add(typeRank1);
        typesValues.add(typeRank2);
        when(dictionaryPersistenceService.getTypeRankSummaryList(dossierTemplateId)).thenReturn(typesValues);

        when(currentApplicationTypeProvider.isDocuMine()).thenReturn(false);
        when(layoutParsingTypeProvider.deferFromCurrentApplicationType()).thenReturn(LayoutParsingType.REDACT_MANAGER_WITHOUT_DUPLICATE_PARAGRAPH);

    }


    @Test
    @SneakyThrows
    public void testDownloadWithDuplicateRanks() {

        // test the export of dossier template
        ExportDownloadRequest exportDownloadRequest = new ExportDownloadRequest("userId", dossierTemplateId);
        Assertions.assertThrows(BadRequestException.class, () -> dossierTemplateManagementService.prepareExportDownload(exportDownloadRequest));

    }


    @Test
    @SneakyThrows
    public void testCloneDossierTemplateWithDuplicateRanks() {

        CloneDossierTemplateRequest cdtr = CloneDossierTemplateRequest.builder().name(dossierTemplateName).cloningUserId("user").build();
        DossierTemplateEntity dossierTemplateEntity = new DossierTemplateEntity();
        dossierTemplateEntity.setId(dossierTemplateId);
        when(dossierTemplateRepository.existsByName(cdtr.getName())).thenReturn(false);
        when(dossierTemplateRepository.findById(dossierTemplateId)).thenReturn(Optional.of(dossierTemplateEntity));
        Assertions.assertThrows(BadRequestException.class, () -> dossierTemplateManagementService.cloneDossierTemplate(dossierTemplateId, cdtr));

    }


    @Test
    @SneakyThrows
    public void testDeduplicateDossierTemplate() {

        List<TypeEntity> allDossierTemplateTypes = new ArrayList<>();
        allDossierTemplateTypes.add(createTypeEntity("type00", dossierTemplateId, 0)); // no change
        allDossierTemplateTypes.add(createTypeEntity("type01", dossierTemplateId, 0)); // new rank will be 1
        allDossierTemplateTypes.add(createTypeEntity("type02", dossierTemplateId, 0)); // new rank will be 2
        allDossierTemplateTypes.add(createTypeEntity("type22", dossierTemplateId, 2)); // new rank will be 3
        allDossierTemplateTypes.add(createTypeEntity("type0", dossierTemplateId, 49)); // no change
        allDossierTemplateTypes.add(createTypeEntity("type1", dossierTemplateId, 50)); // no change
        allDossierTemplateTypes.add(createTypeEntity("type2", dossierTemplateId, 50)); // new rank will be 51
        allDossierTemplateTypes.add(createTypeEntity("type3", dossierTemplateId, 50)); // new rank will be 52
        allDossierTemplateTypes.add(createTypeEntity("type4", dossierTemplateId, 50)); // new rank will be 53
        allDossierTemplateTypes.add(createTypeEntity("type5", dossierTemplateId, 50)); // new rank will be 54
        allDossierTemplateTypes.add(createTypeEntity("type6", dossierTemplateId, 51)); // new rank will be 55
        allDossierTemplateTypes.add(createTypeEntity("type7", dossierTemplateId, 51)); // new rank will be 55
        allDossierTemplateTypes.add(createTypeEntity("type8", dossierTemplateId, 51)); // new rank will be 57
        allDossierTemplateTypes.add(createTypeEntity("type9", dossierTemplateId, 52)); // new rank will be 58
        allDossierTemplateTypes.add(createTypeEntity("type10", dossierTemplateId, 52)); // new rank will be 59
        allDossierTemplateTypes.add(createTypeEntity("type11", dossierTemplateId, 55)); // new rank will be 60
        allDossierTemplateTypes.add(createTypeEntity("type12", dossierTemplateId, 100)); // no change

        List<DossierTemplateEntity> dossierTemplates = new ArrayList<>();
        DossierTemplateEntity dt = new DossierTemplateEntity();
        dt.setId(dossierTemplateId);
        dossierTemplates.add(dt);
        when(dossierTemplateRepository.findAllWhereDeletedIsFalse()).thenReturn(dossierTemplates);
        when(legalBasisMappingPersistenceService.getLegalBasisMapping(dossierTemplateId)).thenReturn(Collections.emptyList());
        when(dictionaryPersistenceService.getAllTypesForDossierTemplate(dossierTemplateId, false)).thenReturn(allDossierTemplateTypes);
        when(dictionaryPersistenceService.getAllDossierTypesForDossierTemplateAndType(dossierTemplateId, "type02", false)).thenReturn(List.of(createTypeEntity("type02",
                                                                                                                                                               dossierTemplateId,
                                                                                                                                                               dossierId,
                                                                                                                                                               0)));

        rankDeDuplicationService.deduplicate();

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type01", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 1);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type02", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 2);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type02", dossierTemplateId, dossierId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 2);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type22", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 3);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type2", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 51);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type3", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 52);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type4", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 53);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type5", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 54);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type6", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 55);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type7", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 56);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type8", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 57);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type9", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 58);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type10", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 59);

        verify(dictionaryPersistenceService, times(1)).updateRankForType(eq(toTypeId("type11", dossierTemplateId)), captor.capture());
        Assertions.assertEquals(captor.getValue(), 60);

    }


    private TypeEntity createTypeEntity(String type, String dossierTemplateId, int rank) {

        return createTypeEntity(type, dossierTemplateId, null, rank);
    }


    private TypeEntity createTypeEntity(String type, String dossierTemplateId, String dossierId, int rank) {

        TypeEntity typeEntity = new TypeEntity();
        typeEntity.setId(toTypeId(type, dossierTemplateId, dossierId));
        typeEntity.setType(type);
        typeEntity.setDossierTemplateId(dossierTemplateId);
        typeEntity.setRank(rank);
        typeEntity.setDescription("description");
        typeEntity.setLabel(type);
        typeEntity.setHexColor("#ddddd");
        typeEntity.setRecommendationHexColor("#cccccc");
        typeEntity.setSkippedHexColor("#cccccc");
        typeEntity.setAddToDictionaryAction(true);

        return typeEntity;
    }

}
