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

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.BaseDictionaryEntry;
import com.iqser.red.service.persistence.management.v1.processor.migration.Migration;
import com.iqser.red.service.persistence.management.v1.processor.service.DictionaryManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.DictionaryService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.EntryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierTemplateRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.DictionaryEntryType;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Setter
@Service
public class TypeToEntityMigration5 extends Migration {

    private static final String NAME = "Migrate false positives, set recommendation color, remove unneeded dictionaries";
    private static final long VERSION = 5;

    @Autowired
    private DictionaryPersistenceService dictionaryPersistenceService;

    @Autowired
    private DossierTemplateRepository dossierTemplateRepository;

    @Autowired
    private EntryPersistenceService entryPersistenceService;

    @Autowired
    private DictionaryManagementService dictionaryManagementService;


    public TypeToEntityMigration5() {

        super(NAME, VERSION);
    }


    @Override
    protected void migrate() {

        var types = dictionaryPersistenceService.getAllTypes(false);
        types.forEach(type -> type.setRecommendationHexColor("#8df06c"));
        dictionaryPersistenceService.saveAllTypes(types);

        var dossierTemplates = dossierTemplateRepository.findAllWhereDeletedIsFalse();

        dossierTemplates.forEach(dossierTemplate -> {
            log.info("Starting false positive migration of dossierTemplate {}", dossierTemplate.getId());
            var typeIdsToDelete = new HashSet<String>();
            var typesOfDossierTemplate = dictionaryPersistenceService.getAllTypes(false);
            var falsePositive = typesOfDossierTemplate.stream().filter(t -> t.getType().equals("false_positive")).findFirst();
            if (falsePositive.isEmpty()) {
                log.info("False positive type does no longer exist in dossierTemplate: {}. Skipping.", dossierTemplate.getName());
                return;
            }
            typeIdsToDelete.add(falsePositive.get().getId());
            var falsePositiveEntries = entryPersistenceService.getEntries(falsePositive.get().getId(), DictionaryEntryType.ENTRY, null)
                    .stream()
                    .map(BaseDictionaryEntry::getValue)
                    .collect(Collectors.toSet());
            log.info("False positive migration of dossierTemplate {} has {} false positive entries", dossierTemplate.getId(), falsePositiveEntries.size());

            typesOfDossierTemplate.stream().filter(t -> !t.getType().equals("false_positive")).forEach(typeEntity -> {
                log.info("Start processing type {}", typeEntity.getType());

                if (typeEntity.getType().startsWith("recommendation_")) {
                    typeIdsToDelete.add(typeEntity.getId());
                    log.info("Finished processing type {}", typeEntity.getType());
                    return;
                }

                var entries = entryPersistenceService.getEntries(typeEntity.getId(), DictionaryEntryType.ENTRY, null)
                        .stream()
                        .map(BaseDictionaryEntry::getValue)
                        .collect(Collectors.toSet());
                log.info("type {} has {} entries", typeEntity.getType(), entries);

                var typeFalsePositives = new HashSet<String>();
                falsePositiveEntries.forEach(falsePositiveValue -> {
                    if (containsAnyWord(falsePositiveValue, entries, typeEntity.isCaseInsensitive())) {
                        typeFalsePositives.add(falsePositiveValue);
                    }
                });

                if (!typeFalsePositives.isEmpty()) {
                    dictionaryManagementService.addEntries(typeEntity.getId(), new ArrayList<>(typeFalsePositives), false, false, DictionaryEntryType.FALSE_POSITIVE);
                    log.info("Added {} for type {}", typeFalsePositives.size(), typeEntity.getType());
                }
                log.info("Finished processing type {}", typeEntity.getType());
            });

            typeIdsToDelete.forEach(typeIdToDelete -> dictionaryManagementService.deleteType(typeIdToDelete));
            log.info("Finished false positive migration of dossierTemplate {}", dossierTemplate.getId());
        });

    }


    public boolean containsAnyWord(String text, Set<String> values, boolean isCaseInsensitive) {

        String inputString = isCaseInsensitive ? text.toLowerCase(Locale.ROOT) : text;

        for (String value : values) {

            String cleanValue = isCaseInsensitive ? value.toLowerCase(Locale.ROOT).trim() : value.trim();

            int startIndex;
            int stopIndex = 0;
            do {
                startIndex = inputString.indexOf(cleanValue, stopIndex);
                stopIndex = startIndex + cleanValue.length();

                if (startIndex > -1 && (startIndex == 0 || Character.isWhitespace(inputString.charAt(startIndex - 1)) || isSeparator(inputString.charAt(startIndex - 1))) && (stopIndex == inputString.length() || isSeparator(
                        inputString.charAt(stopIndex)))) {
                    return true;
                }
            } while (startIndex > -1);
        }
        return false;
    }


    private boolean isSeparator(char c) {

        return Character.isWhitespace(c) || Pattern.matches("\\p{Punct}", String.valueOf(c)) || c == '\"' || c == '‘' || c == '’';
    }

}
