package ru.yandex.webmaster3.viewer.http.searchquery.statistic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Required;

import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.metrics.Category;
import ru.yandex.webmaster3.core.http.ReadAction;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.searchquery.QueryId;
import ru.yandex.webmaster3.core.searchquery.QueryIndicator;
import ru.yandex.webmaster3.core.searchquery.viewer.ViewerQuery;
import ru.yandex.webmaster3.storage.http.searchquery.statistic.StatisticsHelper;
import ru.yandex.webmaster3.storage.searchquery.AccumulatorMap;
import ru.yandex.webmaster3.storage.searchquery.AggregatePeriod;
import ru.yandex.webmaster3.storage.searchquery.QueryStat;
import ru.yandex.webmaster3.storage.searchquery.QueryStatisticsService2;
import ru.yandex.webmaster3.storage.searchquery.RangeFactory;
import ru.yandex.webmaster3.viewer.http.AbstractUserVerifiedHostAction;

/**
 * @author aherman
 */
@ReadAction
@Category("searchquery")
public class GetQueryHistoryAction extends AbstractUserVerifiedHostAction<GetQueryHistoryRequest, QueryStatisticsResponse> {
    private QueryStatisticsService2 queryStatisticsService2;

    @Override
    public QueryStatisticsResponse process(GetQueryHistoryRequest request) throws WebmasterException {
        final LocalDate userRangeStart = request.getLocalDateFrom();
        final LocalDate userRangeEnd = request.getLocalDateTo();
        final List<QueryIndicator> indicators = StatisticsHelper.asList(7, request.getIndicator());
        final List<QueryId> queryIds = StatisticsHelper.asQueryIds(7, request.getQueryId());
        final Set<Integer> regions = StatisticsHelper.asSet(10, request.getRegionId());

        if (queryIds.isEmpty()) {
            return new QueryStatisticsResponse.NormalResponse(null, null, Collections.emptyList());
        }

        final AggregatePeriod period = request.getPeriod();
        RangeSet<LocalDate> rangeSet = RangeFactory.createRanges(userRangeStart, userRangeEnd, period, false, false);

        //noinspection Duplicates
        if (period == AggregatePeriod.WEEK) {
            if (userRangeStart.getYear() == userRangeEnd.getYear()
                    && userRangeStart.getWeekOfWeekyear() == userRangeEnd.getWeekOfWeekyear())
            {
                throw new WebmasterException("Same week",
                        new WebmasterErrorResponse.IllegalParameterValueResponse(this.getClass(), "dateTo", null));
            }
        } else if (period == AggregatePeriod.MONTH) {
            if (userRangeStart.getYear() == userRangeEnd.getYear()
                    && userRangeStart.getMonthOfYear() == userRangeEnd.getMonthOfYear())
            {
                throw new WebmasterException("Same month",
                        new WebmasterErrorResponse.IllegalParameterValueResponse(this.getClass(), "dateTo", null));
            }
        }

        Map<QueryId, String> queryTexts = queryStatisticsService2.getQueryTexts(request.getHostId(), request.getSpecialGroup(), queryIds);
        List<QueryStat> statistics = queryStatisticsService2
                .getStatistics(request.getSpecialGroup(), request.getHostId(),
                        indicators, queryIds,
                        regions, request.getRegionInclusion(),
                        rangeSet.span().lowerEndpoint(), rangeSet.span().upperEndpoint(), request.getDeviceType());

        List<QueryStatisticsResponse.QueryStat> result = new ArrayList<>();

        Map<QueryId, AccumulatorMap> accumulators = new HashMap<>();
        Function<QueryId, AccumulatorMap> accumulatorFactory = q -> AccumulatorMap.create(indicators, rangeSet);
        for (QueryStat queryStat : statistics) {
            AccumulatorMap ac = accumulators.computeIfAbsent(queryStat.getQueryId(), accumulatorFactory);
            ac.apply(queryStat);
        }

        for (QueryId queryId : queryIds) {
//            List<QueryStat> qStat =
//                    statistics.stream().filter(qs -> qs.getQueryId().equals(queryId)).collect(Collectors.toList());
//            AccumulatorMap accumulatorMap = AccumulatorMap.create(indicators, rangeSet);
//            qStat.forEach(accumulatorMap::apply);
            AccumulatorMap accumulatorMap = accumulators.computeIfAbsent(queryId, accumulatorFactory);

            for (QueryIndicator indicator : indicators) {
                List<Pair<Range<LocalDate>, Double>> indicatorStat = accumulatorMap.getIndicator(indicator);

                List<GroupsStatisticResponse.RangeStat> rangeStats = indicatorStat.stream()
                        .map(p -> new GroupsStatisticResponse.RangeStat(
                                p.getKey().lowerEndpoint(),
                                p.getKey().upperEndpoint(),
                                p.getValue(),
                                null))
                        .collect(Collectors.toList());
                AbstractQueryStatisticsResponse.IndicatorStats indicatorStats =
                        new AbstractQueryStatisticsResponse.IndicatorStats(indicator, rangeStats);
                String queryText = queryTexts.get(queryId);
                result.add(new QueryStatisticsResponse.QueryStat(new ViewerQuery(queryId, queryText), indicatorStats));
            }
        }

        return new QueryStatisticsResponse.NormalResponse(null, null, result);
    }

    @Required
    public void setQueryStatisticsService2(QueryStatisticsService2 queryStatisticsService2) {
        this.queryStatisticsService2 = queryStatisticsService2;
    }
}
