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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.stereotype.Service;

import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.Dictionary;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.DictionaryDifference;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.DictionaryDifferencePerType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.DictionaryDifferenceResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.DictionaryEntriesDifference;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.DictionaryFalsePositivesDifference;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.DictionaryFalseRecommendationsDifference;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class DictionaryDifferenceService {

    private final DossierManagementService dossierManagementService;
    private final DictionaryService dictionaryService;


    public DictionaryDifferenceResponse calculatedDictionaryDifference(String dossierTemplateId, Set<String> types) {

        List<DictionaryDifference> dictionaryDifferences = new ArrayList<>();
        List<Dossier> dossiers = dossierManagementService.getAllDossiersForDossierTemplateId(dossierTemplateId, false, false);

        Map<String, Dictionary> templateDictionaryCache = new HashMap<>();

        dossiers.forEach(dossier -> {

            String dossierId = dossier.getId();
            List<DictionaryDifferencePerType> dictionaryDifferencePerTypes = new ArrayList<>();

            types.forEach(type -> {

                // Cache the dossier template dictionary, so we don't load it for each dossier
                Dictionary dossierTemplateDictionary = templateDictionaryCache.computeIfAbsent(type, t -> dictionaryService.getDictionaryForType(t, dossierTemplateId, null));
                Dictionary dossierDictionary = dictionaryService.getMergedDictionaryForType(type, dossierTemplateId, dossierId);
                dictionaryDifferencePerTypes.add(calculateDifference(dossierTemplateDictionary, dossierDictionary, type));
            });

            dictionaryDifferences.add(DictionaryDifference.builder().dossierId(dossierId).dictionaryDifferencePerTypes(dictionaryDifferencePerTypes).build());
        });

        return DictionaryDifferenceResponse.builder().dossierTemplateId(dossierTemplateId).dictionaryDifferences(dictionaryDifferences).build();
    }


    private DictionaryDifferencePerType calculateDifference(Dictionary dossierTemplateDictionary, Dictionary dossierDictionary, String type) {

        Set<String> dossierTemplateDictionaryEntries = new HashSet<>(dossierTemplateDictionary.getEntries());
        Set<String> dossierDictionaryEntries = new HashSet<>(dossierDictionary.getEntries());

        Set<String> dossierTemplateFalsePositiveEntries = new HashSet<>(dossierTemplateDictionary.getFalsePositiveEntries());
        Set<String> dossierFalsePositiveEntries = new HashSet<>(dossierDictionary.getFalsePositiveEntries());

        Set<String> dossierTemplateFalseRecommendationEntries = new HashSet<>(dossierTemplateDictionary.getFalseRecommendationEntries());
        Set<String> dossierFalseRecommendationEntries = new HashSet<>(dossierDictionary.getFalseRecommendationEntries());

        return DictionaryDifferencePerType.builder()
                .type(type)
                .dictionaryEntriesDifference(DictionaryEntriesDifference.builder()
                                                     .addedEntries(calculateAddedEntries(dossierTemplateDictionaryEntries, dossierDictionaryEntries))
                                                     .removedEntries(calculateRemovedEntries(dossierTemplateDictionaryEntries, dossierDictionaryEntries))
                                                     .build())
                .dictionaryFalseRecommendationsDifference(DictionaryFalseRecommendationsDifference.builder()
                                                                  .addedFalseRecommendations(calculateAddedEntries(dossierTemplateFalseRecommendationEntries,
                                                                                                                   dossierFalseRecommendationEntries))
                                                                  .removedFalseRecommendations(calculateRemovedEntries(dossierTemplateFalseRecommendationEntries,
                                                                                                                       dossierFalseRecommendationEntries))
                                                                  .build())
                .dictionaryFalsePositivesDifference(DictionaryFalsePositivesDifference.builder()
                                                            .addedFalsePositives(calculateAddedEntries(dossierTemplateFalsePositiveEntries, dossierFalsePositiveEntries))
                                                            .removedFalsePositives(calculateRemovedEntries(dossierTemplateFalsePositiveEntries, dossierFalsePositiveEntries))
                                                            .build())
                .build();
    }


    private Set<String> calculateAddedEntries(Set<String> dossierTemplateEntries, Set<String> dossierEntries) {

        Set<String> addedEntries = new HashSet<>(dossierEntries);
        addedEntries.removeAll(dossierTemplateEntries);
        return addedEntries;
    }


    private Set<String> calculateRemovedEntries(Set<String> dossierTemplateEntries, Set<String> dossierEntries) {

        Set<String> removedEntries = new HashSet<>(dossierTemplateEntries);
        removedEntries.removeAll(dossierEntries);
        return removedEntries;
    }

}
