package com.iqser.red.service.persistence.management.v1.processor.migration;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;

import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplate;

import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class RankDeDuplicationService {

    private final DossierTemplateManagementService dossierTemplateManagementService;

    private final DictionaryPersistenceService dictionaryPersistenceService;


    @SneakyThrows
    public void deduplicate() {

        dossierTemplateManagementService.getAllDossierTemplates()
                .stream()
                .map(DossierTemplate::getId)
                .forEach(this::deduplicate);
    }


    private void deduplicate(String dossierTemplateId) {

        log.info("deduplicate for dossierTemplateID: {}", dossierTemplateId);
        List<TypeEntity> allDossierTemplateTypes = dictionaryPersistenceService.getAllTypesForDossierTemplate(dossierTemplateId, false);
        TreeMap<Integer, List<TypeEntity>> typesSortedByRank = new TreeMap<>(allDossierTemplateTypes.stream()
                                                                                     .collect(Collectors.groupingBy(TypeEntity::getRank, Collectors.toList())));

        var lastRankOk = -1;
        // we create a map with every rank that needs to be updated and the value is the starting point for that rank
        Map<Integer, Integer> rankToNewRankUpdateMap = new TreeMap<>(Collections.reverseOrder());
        for (var typesByRankEntry : typesSortedByRank.entrySet()) {
            log.debug("Rank: {} - number of types {}", typesByRankEntry.getKey(), typesByRankEntry.getValue().size());
            typesByRankEntry.getValue()
                    .forEach(t -> log.debug("type: {} - dtId: {} - dId: {} typeId: {}", t.getType(), t.getDossierTemplateId(), t.getDossierId(), t.getId()));

            if (typesByRankEntry.getValue().size() > 1) {
                if (typesByRankEntry.getKey() > lastRankOk) {
                    rankToNewRankUpdateMap.put(typesByRankEntry.getKey(), typesByRankEntry.getKey());
                    lastRankOk = typesByRankEntry.getKey() + typesByRankEntry.getValue().size() - 1;
                } else {
                    rankToNewRankUpdateMap.put(typesByRankEntry.getKey(), lastRankOk + 1);
                    lastRankOk = lastRankOk + typesByRankEntry.getValue().size();
                }
            } else {
                if (typesByRankEntry.getKey() > lastRankOk) {
                    lastRankOk = typesByRankEntry.getKey();
                } else {
                    lastRankOk = lastRankOk + 1;
                    rankToNewRankUpdateMap.put(typesByRankEntry.getKey(), lastRankOk);
                }
            }
        }
        // need to update in reverse order so we don't get exception for duplicate ranks
        for (var rankToUpdateEntry : rankToNewRankUpdateMap.entrySet()) {
            var newRank = rankToUpdateEntry.getValue();
            for (TypeEntity t : typesSortedByRank.get(rankToUpdateEntry.getKey())) {

                if (newRank != t.getRank()) {
                    log.info("Type {} with rank: {} will be updated to rank: {} - typeId {}", t.getType(), t.getRank(), newRank, t.getId());
                    dictionaryPersistenceService.updateRankForType(t.getId(), newRank);
                    var dossierTypes = dictionaryPersistenceService.getAllDossierTypesForDossierTemplateAndType(dossierTemplateId, t.getType(), false);

                    for (TypeEntity td : dossierTypes) {
                        log.info("Type {} with rank: {} will be updated to rank: {} - typeId {}", td.getType(), td.getRank(), newRank, td.getId());
                        dictionaryPersistenceService.updateRankForType(td.getId(), newRank);
                    }

                }
                newRank = newRank + 1;
            }
        }
    }

}
