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.IndicatorUsage;
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.stream.Collectors;

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

    static final List<QueryIndicator> DEFAULT_INDICATORS = Arrays.asList(QueryIndicator.values()).stream()
            .filter(qi -> qi.isUsable(IndicatorUsage.IN_STATISTICS))
            .collect(Collectors.toList());

    @Override
    public UrlStatisticsResponse process(GetSelectedUrlRequest request) throws WebmasterException {
        final LocalDate userRangeStart = request.getLocalDateFrom();
        final LocalDate userRangeEnd = request.getLocalDateTo();
        final Set<Integer> regions = StatisticsHelper.asSet(10, request.getRegionId());
        Optional<PathId> pathIdO = PathId.fromStringId(request.getPathId());
        if (!pathIdO.isPresent()) {
            throw new WebmasterException("Error",
                    new WebmasterErrorResponse.IllegalParameterValueResponse(this.getClass(), "pathId", request.getPathId()));
        }
        List<PathId> pathIds = Collections.singletonList(pathIdO.get());

        final AggregatePeriod period = request.getPeriod();

        RangeSet<LocalDate> userRangeSet = RangeFactory.doubleRange(userRangeStart, userRangeEnd);
        RangeSet<LocalDate> periodRangeSet = RangeFactory.createRanges(userRangeEnd, period, request.getMaxRanges() + 1, false);

        Map<PathId, String> idToPathText;
        idToPathText = urlStatisticsService2.getPathTexts(request.getHostId(), request.getSpecialGroup(), pathIds);

        Range<LocalDate> pathRange = userRangeSet.span().span(periodRangeSet.span());
        List<PathStat> dayStat = urlStatisticsService2
                .getStatistics(request.getSpecialGroup(), request.getHostId(),
                        DEFAULT_INDICATORS, pathIds,
                        regions, request.getRegionInclusion(),
                        pathRange.lowerEndpoint(), pathRange.upperEndpoint(), request.getDeviceType());

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

        String pathText = idToPathText.get(pathIdO.get());

        AccumulatorMap userRangeMap = AccumulatorMap.create(DEFAULT_INDICATORS, userRangeSet);
        AccumulatorMap periodMap = AccumulatorMap.create(DEFAULT_INDICATORS, periodRangeSet);
        dayStat.forEach(ds -> {
            userRangeMap.apply(ds);
            periodMap.apply(ds);
        });
        for (QueryIndicator indicator : DEFAULT_INDICATORS) {
            List<GroupsStatisticResponse.RangeStat> rangeStats = new ArrayList<>();
            List<GroupsStatisticResponse.RangeStat> wholeRangeStats = new ArrayList<>();

            {
                List<Pair<Range<LocalDate>, Double>> periodIndicatorStat = periodMap.getIndicator(indicator);
                MapWithDiff.map(periodIndicatorStat.iterator(), (r, value, diff) -> {
                    rangeStats.add(new AbstractQueryStatisticsResponse.RangeStat(r.lowerEndpoint(), r.upperEndpoint(),
                            value, diff));
                });
            }
            {
                List<Pair<Range<LocalDate>, Double>> userIndicatorStat = userRangeMap.getIndicator(indicator);
                MapWithDiff.map(userIndicatorStat.iterator(), (r, value, diff) -> {
                    wholeRangeStats.add(new AbstractQueryStatisticsResponse.RangeStat(r.lowerEndpoint(), r.upperEndpoint(),
                            value, diff));
                });
            }

            rangeStats.addAll(wholeRangeStats);
            Collections.reverse(rangeStats);

            AbstractQueryStatisticsResponse.IndicatorStats indicatorStats =
                    new AbstractQueryStatisticsResponse.IndicatorStats(indicator, rangeStats);

            result.add(new UrlStatisticsResponse.PathStat(new ViewerPath(pathIdO.get(), pathText), indicatorStats));
        }

        List<UrlStatisticsResponse.DateRange> ranges = periodRangeSet.asRanges().stream()
                .skip(1)
                .map(e -> new UrlStatisticsResponse.DateRange(e.lowerEndpoint(), e.upperEndpoint(), false))
                .collect(Collectors.toList());
        ranges.add(new UrlStatisticsResponse.DateRange(userRangeStart, userRangeEnd, true));
        Collections.reverse(ranges);

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