package ru.yandex.webmaster3.viewer.regions;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.IntPredicate;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;

import ru.yandex.webmaster3.core.data.W3RegionInfo;
import ru.yandex.webmaster3.core.regions.RegionUtils;
import ru.yandex.webmaster3.core.regions.W3RegionsTreeService;
import ru.yandex.wmtools.common.error.InternalException;

/**
 * @author avhaliullin
 */
public class SearchQueryRegionsIndexService {
    private static final Logger log = LoggerFactory.getLogger(SearchQueryRegionsIndexService.class);

    // Мапка из родителя X и потомка Y в Z - где Z - прямой ребенок X, и содержит в поддереве Y
    private final Map<Integer, Map<Integer, Integer>> parent2Child2DirectParentsChild = new HashMap<>();
    private final Map<Integer, Collection<String>> regionId2LowerCaseNames = new HashMap<>();

    private W3RegionsTreeService w3regionsTreeService;

    public Map<Integer, Integer> getChild2ParentsDirectChild(int parent) {
        return parent2Child2DirectParentsChild.get(parent);
    }

    public IntPredicate regionNameSubstringPredicate(String substring) {
        if (StringUtils.isEmpty(substring)) {
            return x -> true;
        } else {
            String lcFilter = substring.trim().toLowerCase();
            return r -> {
                Collection<String> lcNames = regionId2LowerCaseNames.getOrDefault(r, Collections.emptyList());
                for (String lcName : lcNames) {
                    if (lcName.contains(lcFilter)) {
                        return true;
                    }
                }
                return false;
            };
        }
    }

    public NameMatchType match(String filter, int regionId) {
        Collection<String> lcNames = regionId2LowerCaseNames.getOrDefault(regionId, Collections.emptyList());
        NameMatchType result = NameMatchType.NONE;
        for (String lcName : lcNames) {
            if (lcName.contains(filter)) {
                result = NameMatchType.MATCH;
                if (lcName.equals(filter)) {
                    return NameMatchType.EXACT_MATCH;
                }
            }
        }
        return result;
    }

    public void init() throws InternalException {
        buildLowerCaseNames();
        buildIndirectParentRelations();
    }

    private void buildLowerCaseNames() {
        w3regionsTreeService.getAllVisibleRegions(RegionUtils.VISIBLE_OR_WHOLE_WORLD_PREDICATE).forEach(
                region -> regionId2LowerCaseNames.put(
                        region.getId(),
                        region.getNames().values().stream().map(String::toLowerCase).collect(Collectors.toList())
                )
        );
    }

    private void buildIndirectParentRelations() {
        w3regionsTreeService.getAllVisibleRegions(RegionUtils.VISIBLE_OR_WHOLE_WORLD_PREDICATE).forEach(
                r -> addToChildAndParent2DirectChildMapping(r.getId(), r.getId())
        );
    }

    private void addToChildAndParent2DirectChildMapping(int child, int parent) {
        W3RegionInfo grandParent = w3regionsTreeService.getVisibleParent(parent, RegionUtils.VISIBLE_OR_WHOLE_WORLD_PREDICATE);
        if (grandParent != null && grandParent.getId() != parent) {
            parent2Child2DirectParentsChild.computeIfAbsent(grandParent.getId(), x -> new HashMap<>(4)).put(child, parent);
            addToChildAndParent2DirectChildMapping(child, grandParent.getId());
        }
    }

    public enum NameMatchType {
        NONE(false, false),
        MATCH(true, false),
        EXACT_MATCH(true, true),;

        private final boolean match;
        private final boolean exactMatch;

        NameMatchType(boolean match, boolean exactMatch) {
            this.match = match;
            this.exactMatch = exactMatch;
        }

        public boolean isMatch() {
            return match;
        }

        public boolean isExactMatch() {
            return exactMatch;
        }
    }

    @Required
    public void setW3regionsTreeService(W3RegionsTreeService w3regionsTreeService) {
        this.w3regionsTreeService = w3regionsTreeService;
    }
}
