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

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

import com.fasterxml.jackson.annotation.JsonInclude;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.http.ReadAction;
import ru.yandex.webmaster3.core.metrics.Category;
import ru.yandex.webmaster3.core.searchquery.QueryGroupId;
import ru.yandex.webmaster3.core.searchquery.QueryIndicator;
import ru.yandex.webmaster3.core.searchquery.viewer.ViewerQueryGroup;
import ru.yandex.webmaster3.storage.searchquery.AccumulatorMap;
import ru.yandex.webmaster3.storage.searchquery.AggregatePeriod;
import ru.yandex.webmaster3.storage.searchquery.DeviceType;
import ru.yandex.webmaster3.storage.searchquery.GroupStat;
import ru.yandex.webmaster3.storage.searchquery.GroupStatisticsService2;
import ru.yandex.webmaster3.storage.searchquery.QueryGroupService;
import ru.yandex.webmaster3.storage.searchquery.RangeFactory;
import ru.yandex.webmaster3.storage.searchquery.RegionInclusion;
import ru.yandex.webmaster3.storage.searchquery.util.Accumulator;
import ru.yandex.webmaster3.viewer.http.searchquery.statistic.AbstractQueryStatisticsResponse;
import ru.yandex.webmaster3.viewer.http.searchquery.statistic.GetGroupHistoryAction;
import ru.yandex.webmaster3.viewer.http.searchquery.statistic.GroupsStatisticResponse;

/**
 * @author Oleg Bazdyrev
 */
@ReadAction
@Category("searchquery")
@Component("/searchquery/statistics/rivals/history")
public class GetRivalsHistoryAction extends GetGroupHistoryAction {
    // TODO костыль для нормировки только определенных индикаторов
    private static final EnumSet<QueryIndicator> INDICATORS_TO_NORMALIZE = EnumSet.of(
            QueryIndicator.WEIGHTED_CLICKS,
            QueryIndicator.WEIGHTED_RIVALS_CLICKS);


    @Autowired
    public GetRivalsHistoryAction(QueryGroupService queryGroupService, GroupStatisticsService2 groupStatisticsService2) {
        super(queryGroupService, groupStatisticsService2);
    }

    @NotNull
    protected List<GroupsStatisticResponse.GroupStat> getStatistics(WebmasterHostId hostId,
                                                                    List<QueryGroupId> groupIds, List<QueryIndicator> indicators,
                                                                    LocalDate userRangeStart, LocalDate userRangeEnd, AggregatePeriod period, RegionInclusion regionInclusion,
                                                                    Set<Integer> regionIds, DeviceType deviceType) {
        Map<QueryGroupId, ViewerQueryGroup> groups = resolveQueryGroups(groupIds, queryGroupService);

        RangeSet<LocalDate> ranges = RangeFactory.createRanges(userRangeStart, userRangeEnd, period, false, false);
        boolean firstRangeIsFull = RangeFactory.getPeriodStart(userRangeStart, period).equals(userRangeStart);

        LocalDate normUpperEndpoint = ranges.span().lowerEndpoint();
        LocalDate normLowerEndpoint = normUpperEndpoint.minusMonths(1);
        Pair<LocalDate, LocalDate> queryRange = Pair.of(normLowerEndpoint, ranges.span().upperEndpoint());
        Map<QueryGroupId, List<GroupStat>> statsAll = groupStatisticsService2.getStatistics(hostId,
                queryRange,
                regionInclusion, regionIds,
                groups.keySet(), indicators, deviceType);

        Map<QueryGroupId, List<GroupStat>> statsCalc = new HashMap<>();
        Map<QueryIndicator, Accumulator> normAccumulators = GroupStatisticsService2.getAccumulators(indicators);
        GroupStatisticsService2.separateNormStats(statsAll, normLowerEndpoint, normUpperEndpoint, normUpperEndpoint, statsCalc, normAccumulators);
        Map<QueryIndicator, Double> normFactors = GroupStatisticsService2.getNormFactors(normAccumulators);
        List<GroupsStatisticResponse.GroupStat> result = new ArrayList<>();

        for (QueryGroupId groupId : groupIds) {
            ViewerQueryGroup viewerQueryGroup = groups.get(groupId);
            if (viewerQueryGroup == null) {
                continue;
            }
            AccumulatorMap accumulatorMapCalc = AccumulatorMap.create(indicators, ranges);
            List<GroupStat> listStatsCalc = statsCalc.getOrDefault(groupId, Collections.emptyList());
            listStatsCalc.forEach(accumulatorMapCalc::apply);
            Map<QueryIndicator, Double> fallbackNormFactors = accumulatorMapCalc.findFirstNotZeroValue(
                    CollectionUtils.retainAll(INDICATORS_TO_NORMALIZE, indicators), firstRangeIsFull ? 0 : 1);

            for (QueryIndicator indicator : indicators) {
                Double normFactor = normFactors.get(indicator);
                if (normFactor == null) {
                    normFactor = fallbackNormFactors.get(indicator);
                }
                List<Pair<Range<LocalDate>, Double>> indicatorStat = accumulatorMapCalc
                        .getIndicatorNormalized(indicator, normFactor);
                List<AbstractQueryStatisticsResponse.RangeStat> rs = indicatorStat.stream().map(e -> createRangeStat(e.getKey(), e.getValue()))
                        .collect(Collectors.toList());
                result.add(new GroupsStatisticResponse.GroupStat(viewerQueryGroup,
                        new GroupsStatisticResponse.IndicatorStats(indicator, rs)));
            }
        }
        return result;
    }

    @Override
    protected AbstractQueryStatisticsResponse.RangeStat createRangeStat(Range<LocalDate> range, Double value) {
        return new RangeStat(range.lowerEndpoint(), range.upperEndpoint(), value, null);
    }

    public static class RangeStat extends AbstractQueryStatisticsResponse.RangeStat {

        public RangeStat(LocalDate dateFrom, LocalDate dateTo, Double value, Double difference) {
            super(dateFrom, dateTo, value, difference);
        }

        @Override
        @JsonInclude(JsonInclude.Include.ALWAYS)
        public Double getValue() {
            return super.getValue();
        }
    }
}
