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.Required;
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.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.MapWithDiff;
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;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

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

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

    @Override
    public QueryStatisticsResponse process(GetSelectedQueryRequest request) throws WebmasterException {
        final LocalDate userRangeStart = request.getLocalDateFrom();
        final LocalDate userRangeEnd = request.getLocalDateTo();
        final Set<Integer> regions = StatisticsHelper.asSet(10, request.getRegionId());
        Optional<QueryId> queryIdO = QueryId.fromStringId(request.getQueryId());
        if (!queryIdO.isPresent()) {
            throw new WebmasterException("Error",
                    new WebmasterErrorResponse.IllegalParameterValueResponse(this.getClass(), "queryId", request.getQueryId()));
        }
        List<QueryId> queryIds = Collections.singletonList(queryIdO.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<QueryId, String> idToQueryText;
        idToQueryText = queryStatisticsService2.getQueryTexts(request.getHostId(), request.getSpecialGroup(), queryIds);
        ;
        Range<LocalDate> queryRange = userRangeSet.span().span(periodRangeSet.span());
        List<QueryStat> dayStat = queryStatisticsService2
                .getStatistics(request.getSpecialGroup(), request.getHostId(),
                        DEFAULT_INDICATORS, queryIds,
                        regions, request.getRegionInclusion(),
                        queryRange.lowerEndpoint(), queryRange.upperEndpoint(), request.getDeviceType());

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

        String queryText = idToQueryText.get(queryIdO.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 QueryStatisticsResponse.QueryStat(new ViewerQuery(queryIdO.get(), queryText), indicatorStats));
        }

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

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

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