package ru.yandex.webmaster3.storage.searchquery;

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.searchquery.*;
import ru.yandex.webmaster3.storage.searchquery.dao.*;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author aherman
 */
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class QueryStatisticsService2 {

    private final FavoriteValuesCHDao mdbFavoriteValuesCHDao;
    private final Top3000QueriesCHDao mdbTop3000QueriesCHDao;
    private final Top3000ValuesCHDao mdbTop3000ValuesCHDao;
    private final QueriesYDao queriesYDao;
    private final QueriesGroupsRelationYDao queriesGroupsRelationYDao;

    public List<QueryStat> getStatistics(SpecialGroup specialGroup, WebmasterHostId hostId,
            List<QueryIndicator> indicators, List<QueryId> queryIds,
            Set<Integer> regions, RegionInclusion regionInclusion,
            LocalDate dateFrom, LocalDate dateTo, DeviceType deviceType)
    {
        if (queryIds.isEmpty()){
            return Collections.emptyList();
        }
        switch (specialGroup) {
            case TOP_3000_QUERIES:
                return mdbTop3000ValuesCHDao.getQueryStat(
                        hostId,
                        indicators,
                        dateFrom, dateTo,
                        regionInclusion, regions,
                        queryIds, deviceType);
            case SELECTED_QUERIES:
                return mdbFavoriteValuesCHDao.getQueryStat(
                        hostId,
                        dateFrom, dateTo,
                        regionInclusion, regions,
                        queryIds, indicators, deviceType);
            default:
                return Collections.emptyList();
        }
    }

    public Map<QueryId, String> getQueryTexts(WebmasterHostId hostId, SpecialGroup specialGroup, List<QueryId> queryIds) {
        if (queryIds.isEmpty()){
            return Collections.emptyMap();
        }
        switch (specialGroup) {
            case TOP_3000_QUERIES:
                Map<QueryId, String> idToQueryText = mdbTop3000QueriesCHDao.getQueries(hostId, queryIds)
                        .stream()
                        .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
                return idToQueryText;
            case SELECTED_QUERIES:
                return queriesYDao.get(hostId, new HashSet<>(queryIds)).stream().collect(Collectors.toMap(Query::getQueryId, Query::getQuery));
            default:
                return Collections.emptyMap();
        }
    }

    public List<QueryId> getQueryIds(WebmasterHostId hostId, SpecialGroup specialGroup,
            Set<Integer> regions, RegionInclusion regionInclusion,
            LocalDate userRangeStart, LocalDate userRangeEnd,
            List<QueryId> excludeQueries, DeviceType deviceType,
            QueryIndicator orderBy, OrderDirection orderDirection,
            int startFrom, int limit)
    {
        switch (specialGroup) {
            case TOP_3000_QUERIES:
                return mdbTop3000ValuesCHDao.getQueryIds(
                        hostId,
                        userRangeStart, userRangeEnd,
                        excludeQueries,
                        regionInclusion, regions,
                        deviceType,
                        orderBy, orderDirection,
                        startFrom, limit);

            case SELECTED_QUERIES:
                return getFavoriteQueryIds(hostId,
                        regionInclusion, regions,
                        userRangeStart, userRangeEnd,
                        excludeQueries, deviceType,
                        orderBy, orderDirection,
                        startFrom, limit);
            default:
                return Collections.emptyList();
        }
    }

    private List<QueryId> getFavoriteQueryIds(WebmasterHostId hostId,
            RegionInclusion regionInclusion, Set<Integer> regions,
            LocalDate userRangeStart, LocalDate userRangeEnd,
            List<QueryId> excludeQueries, DeviceType deviceType,
            QueryIndicator orderBy, OrderDirection orderDirection,
            int startFrom, int limit) {
        Set<QueryId> allQueryIds = queriesGroupsRelationYDao.getQueries(QueryGroupId.byGroupIdStr(hostId, SpecialGroup.SELECTED_QUERIES.getGroupId()));
        allQueryIds.removeAll(excludeQueries);
        if (allQueryIds.isEmpty()) {
            return Collections.emptyList();
        }
        return mdbFavoriteValuesCHDao.getQueryIds(
                hostId,
                allQueryIds,
                userRangeStart, userRangeEnd,
                regionInclusion, regions,
                orderBy, orderDirection,
                startFrom, limit, deviceType
        );
    }

    public int countQueries(WebmasterHostId hostId, SpecialGroup specialGroup,
            Set<Integer> regions, RegionInclusion regionInclusion,
            LocalDate userRangeStart, LocalDate userRangeEnd, DeviceType deviceType)
    {
        switch (specialGroup) {
            case TOP_3000_QUERIES:
                return mdbTop3000ValuesCHDao.countQueries(
                        hostId,
                        userRangeStart, userRangeEnd,
                        regionInclusion, regions,
                        deviceType
                );
            case SELECTED_QUERIES:
                return queriesGroupsRelationYDao.getGroupSize(QueryGroupId.byGroupIdStr(hostId, specialGroup.getGroupId()));
            default:
                return 0;
        }
    }

    public Pair<Map<QueryId, String>, List<QueryStat>> getQueryStatistics(WebmasterHostId hostId,
            SpecialGroup specialGroup,
            List<QueryIndicator> indicators,
            RegionInclusion regionInclusion, Set<Integer> regions,
            List<QueryId> queryIds,
            LocalDate rangeStart, LocalDate rangeEnd, DeviceType deviceType)
    {
        Map<QueryId, String> idToQueryText;
        List<QueryStat> queryStat;
        switch (specialGroup) {
            case TOP_3000_QUERIES:
                idToQueryText = mdbTop3000QueriesCHDao.getQueries(hostId, queryIds)
                        .stream()
                        .collect(Collectors.toMap(Pair::getKey, Pair::getValue));

                queryStat = mdbTop3000ValuesCHDao.getQueryStat(
                        hostId,
                        indicators,
                        rangeStart, rangeEnd,
                        regionInclusion, regions,
                        queryIds, deviceType);
                break;

            case SELECTED_QUERIES:
                idToQueryText = queriesYDao.get(hostId, new HashSet<>(queryIds)).stream()
                        .collect(Collectors.toMap(Query::getQueryId, Query::getQuery));
                queryStat = mdbFavoriteValuesCHDao.getQueryStat(
                        hostId,
                        rangeStart, rangeEnd,
                        regionInclusion, regions,
                        queryIds, indicators, deviceType);
                break;

            default:
                idToQueryText = Collections.emptyMap();
                queryStat = Collections.emptyList();
        }
        return Pair.of(idToQueryText, queryStat);
    }

    public List<Query> suggestQuery(WebmasterHostId hostId, SpecialGroup specialGroup,
                                    String queryFilter,
                                    int pageSize) {
        switch (specialGroup) {
            case TOP_3000_QUERIES:
                return mdbTop3000QueriesCHDao.suggestQueries(hostId, queryFilter, pageSize);
            case SELECTED_QUERIES:
                Set<QueryId> queryIds = queriesGroupsRelationYDao.getQueries(QueryGroupId.byGroupIdStr(hostId, specialGroup.getGroupId()));
                Stream<Query> qStream = queriesYDao.get(hostId, queryIds)
                        .stream();
                if (!StringUtils.isEmpty(queryFilter)) {
                    qStream = qStream.filter(q -> q.getQuery().contains(queryFilter));
                }
                return qStream
                        .sorted(Comparator.comparing(Query::getQuery))
                        .limit(pageSize)
                        .collect(Collectors.toList());
            default:
                return Collections.emptyList();
        }
    }

    public boolean isValidQueryId(SpecialGroup specialGroup, WebmasterHostId hostId, QueryId queryId) {
        switch (specialGroup) {
            case TOP_3000_QUERIES:
                return !mdbTop3000QueriesCHDao.getQueries(hostId, List.of(queryId)).isEmpty();

            case SELECTED_QUERIES:
                return queriesYDao.get(hostId, queryId) != null;
        }

        return false;
    }
}
