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

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.Autowired;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.http.ReadAction;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.metrics.Category;
import ru.yandex.webmaster3.core.searchquery.PathId;
import ru.yandex.webmaster3.core.searchquery.QueryIndicator;
import ru.yandex.webmaster3.core.searchquery.viewer.ViewerPath;
import ru.yandex.webmaster3.storage.http.searchquery.statistic.StatisticsHelper;
import ru.yandex.webmaster3.storage.searchquery.*;
import ru.yandex.webmaster3.viewer.http.AbstractUserVerifiedHostAction;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author lester
 */
@ReadAction
@Category("searchquery")
public class GetUrlHistoryAction extends AbstractUserVerifiedHostAction<GetUrlHistoryRequest, UrlStatisticsResponse> {
    @Autowired
    private UrlStatisticsService2 urlStatisticsService2;

    @Override
    public UrlStatisticsResponse process(GetUrlHistoryRequest request) throws WebmasterException {
        final LocalDate userRangeStart = request.getLocalDateFrom();
        final LocalDate userRangeEnd = request.getLocalDateTo();
        final List<QueryIndicator> indicators = StatisticsHelper.asList(7, request.getIndicator());
        final List<PathId> pathIds = StatisticsHelper.asPathIds(7, request.getPathId());
        final Set<Integer> regions = StatisticsHelper.asSet(10, request.getRegionId());

        if (pathIds.isEmpty()) {
            return new UrlStatisticsResponse.NormalResponse(null, null, Collections.emptyList());
        }

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

        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<PathId, String> pathTexts = urlStatisticsService2.getPathTexts(request.getHostId(), request.getSpecialGroup(), pathIds);
        List<PathStat> statistics = urlStatisticsService2
                .getStatistics(request.getSpecialGroup(), request.getHostId(),
                        indicators, pathIds,
                        regions, request.getRegionInclusion(),
                        rangeSet.span().lowerEndpoint(), rangeSet.span().upperEndpoint(), request.getDeviceType());

        List<UrlStatisticsResponse.PathStat> result = new ArrayList<>();

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

        for (PathId pathId : pathIds) {
            AccumulatorMap accumulatorMap = accumulators.computeIfAbsent(pathId, 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 pathText = pathTexts.get(pathId);
                result.add(new UrlStatisticsResponse.PathStat(new ViewerPath(pathId, pathText), indicatorStats));
            }
        }

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