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

import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Component;

import ru.yandex.webmaster3.core.WebmasterException;
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.OrderDirection;
import ru.yandex.webmaster3.core.searchquery.QueryGroup;
import ru.yandex.webmaster3.core.searchquery.QueryGroupId;
import ru.yandex.webmaster3.core.searchquery.QueryIndicator;
import ru.yandex.webmaster3.core.searchquery.viewer.QueryGroupConverter;
import ru.yandex.webmaster3.core.searchquery.viewer.ViewerQueryGroup;
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 static ru.yandex.webmaster3.core.searchquery.SpecialGroup.ALL_QUERIES;
import static ru.yandex.webmaster3.core.searchquery.SpecialGroup.SELECTED_QUERIES;

/**
 * @author aherman
 */
@ReadAction
@Category("searchquery")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Component("/searchquery/statistics/group/list")
public class GetGroupListStatisticsAction extends
        AbstractUserVerifiedHostAction<GetGroupListStatisticsRequest, GroupsStatisticResponse> {
    private static final Logger log = LoggerFactory.getLogger(GetGroupStatisticsAction.class);
    private final QueryGroupService queryGroupService;
    private final GroupStatisticsService2 groupStatisticsService2;

    protected static Comparator<GroupsStatisticResponse.GroupStat> COMPARE_BY_NAME =
            Comparator.comparing(g -> StringUtils.defaultString(g.getGroup().getName()));

    @Override
    public GroupsStatisticResponse process(GetGroupListStatisticsRequest request) throws WebmasterException {
        final LocalDate userRangeStart = request.getLocalDateFrom();
        final LocalDate userRangeEnd = request.getLocalDateTo();
        final List<QueryIndicator> indicators = StatisticsHelper.asList(7, request.getIndicator());
        final Set<Integer> regionIds = StatisticsHelper.asSet(10, request.getRegionId());

        RangeSet<LocalDate> rangeSet = RangeFactory.doubleRange(userRangeStart, userRangeEnd);

        Map<QueryGroupId, ViewerQueryGroup> groups = new HashMap<>();
        WebmasterHostId hostId = request.getHostId();
        groups.put(new QueryGroupId(hostId, ALL_QUERIES), QueryGroupConverter.toViewerGroup(ALL_QUERIES));
        groups.put(new QueryGroupId(hostId, SELECTED_QUERIES), QueryGroupConverter.toViewerGroup(SELECTED_QUERIES));
        List<QueryGroup> queryGroups = queryGroupService.listGroups(hostId);
        for (QueryGroup group : queryGroups) {
            groups.put(group.getQueryGroupId(), QueryGroupConverter.toViewerGroup(group));
        }

        Map<QueryGroupId, List<GroupStat>> stats = groupStatisticsService2.getStatistics(hostId,
                Pair.of(rangeSet.span().lowerEndpoint(), rangeSet.span().upperEndpoint()),
                request.getRegionInclusion(), regionIds,
                groups.keySet(), indicators, request.getDeviceType());

        List<GroupsStatisticResponse.GroupStat> result = new ArrayList<>();
        for (Map.Entry<QueryGroupId, ViewerQueryGroup> entry : groups.entrySet()) {
            try {
                AccumulatorMap accumulatorMap = AccumulatorMap.create(indicators, rangeSet);
                List<GroupStat> groupStats = stats.getOrDefault(entry.getKey(), Collections.emptyList());
                groupStats.forEach(accumulatorMap::apply);
                List<GroupsStatisticResponse.IndicatorStats> groupStatResult = new ArrayList<>();
                for (QueryIndicator indicator : indicators) {
                    List<Pair<Range<LocalDate>, Double>> indicatorStat = accumulatorMap.getIndicator(indicator);
                    List<AbstractQueryStatisticsResponse.RangeStat> rs = new ArrayList<>();
                    MapWithDiff.map(indicatorStat.iterator(), (r, current, diff) ->
                            rs.add(new AbstractQueryStatisticsResponse.RangeStat(r.lowerEndpoint(),
                                    r.upperEndpoint(),
                                    current, diff)
                            )
                    );
                    groupStatResult.add(new AbstractQueryStatisticsResponse.IndicatorStats(indicator, rs));
                }
                GroupsStatisticResponse.GroupStat gs =
                        new GroupsStatisticResponse.GroupStat(entry.getValue(), groupStatResult);
                result.add(gs);
            } catch (Exception e) {
                log.error("Error", e);
            }
        }

        QueryIndicator orderBy = request.getOrderBy();
        OrderDirection orderDirection = request.getOrderDirection();
        if (orderBy == null) {
            orderBy = QueryIndicator.NAME;
            orderDirection = OrderDirection.ASC;
        }

        Comparator<GroupsStatisticResponse.GroupStat> comparator = GroupComparators.SPECIAL_GROUP_FIRST;
        if (orderBy == QueryIndicator.NAME) {
            comparator = comparator.thenComparing(COMPARE_BY_NAME);
        } else {
            Comparator<GroupsStatisticResponse.GroupStat> ic =
                    comparator.thenComparing(GroupComparators.comparatorByIndicator(orderBy));
            ic = orderDirection == OrderDirection.ASC ? ic : ic.reversed();
            comparator = comparator.thenComparing(ic);
        }
        Collections.sort(result, comparator);
        return new GroupsStatisticResponse.NormalResponse(null, result);
    }
}
