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

import com.google.protobuf.InvalidProtocolBufferException;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import ru.yandex.webmaster3.core.metrics.Category;
import ru.yandex.webmaster3.core.http.ReadAction;
import ru.yandex.webmaster3.core.searchquery.OrderDirection;
import ru.yandex.webmaster3.core.searchquery.QueryId;
import ru.yandex.webmaster3.core.searchquery.QueryIndicator;
import ru.yandex.webmaster3.core.searchquery.SpecialGroup;
import ru.yandex.webmaster3.core.searchquery.viewer.ViewerQuery;
import ru.yandex.webmaster3.core.util.TimeUtils;
import ru.yandex.webmaster3.core.proto.ProtobufUtils;
import ru.yandex.webmaster3.proto.dashboard.cache.DashboardCache;
import ru.yandex.webmaster3.storage.cache.DashboardCacheService;
import ru.yandex.webmaster3.storage.cache.DashboardType;
import ru.yandex.webmaster3.storage.searchquery.RegionInclusion;
import ru.yandex.webmaster3.viewer.util.DashboardUtils;
import ru.yandex.webmaster3.storage.http.searchquery.statistic.StatisticsHelper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * @author aherman
 */
@ReadAction
@Category("searchquery")
public class GetQueryListStatisticsDashboardAction extends GetQueryListStatisticsAction {
    private static final Logger log = LoggerFactory.getLogger(GetQueryListStatisticsDashboardAction.class);

    private DashboardCacheService dashboardCacheService;

    @Override
    public GetQueryListStatisticsResponse process(GetQueryListStatisticsRequest request) {
        final SpecialGroup specialGroup = request.getSpecialGroup();
        final DashboardType dashboardType;
        switch (specialGroup) {
            case TOP_3000_QUERIES:
                dashboardType = DashboardType.SEARCH_QUERY_LIST_TOP;
                break;
            case SELECTED_QUERIES:
                dashboardType = DashboardType.SEARCH_QUERY_LIST_FAVORITE;
                break;
            default:
                return new GetQueryListStatisticsResponse.NormalResponse(0, Collections.emptyList());
        }

        final LocalDate userRangeStart = request.getLocalDateFrom();
        final LocalDate userRangeEnd = request.getLocalDateTo();
        final boolean withDiff = false;
        final List<QueryId> userSelectedQueryIds = Collections.emptyList();
        final List<QueryIndicator> indicators = StatisticsHelper.asList(7, request.getIndicator());
        final Set<Integer> regions = Collections.emptySet();
        final RegionInclusion regionInclusion = request.getRegionInclusion();
        final QueryIndicator orderBy = request.getOrderBy();
        final OrderDirection orderDirection = request.getOrderDirection();

        String cacheKey = createKey(userRangeStart, userRangeEnd);

        try {
            Optional<List<GetQueryListStatisticsResponse.QueryStat>> cached =
                    dashboardCacheService.getData(request.getHostId(), dashboardType, cacheKey, (valueBytes) -> {
                        DashboardCache.QueryListDashboardCache qldc;
                        try {
                            qldc = DashboardCache.QueryListDashboardCache.parseFrom(valueBytes);
                        } catch (InvalidProtocolBufferException e) {
                            log.error("Unable to read value", e);
                            return Optional.empty();
                        }
                        List<GetQueryListStatisticsResponse.QueryStat> result = new ArrayList<>();
                        for (int i = 0; i < qldc.getQueriesCount(); i++) {
                            DashboardCache.QueryListDashboardCache.QueryStatistics qs = qldc.getQueries(i);
                            List<AbstractQueryStatisticsResponse.IndicatorStats> isr = new ArrayList<>();
                            for (int j = 0; j < qs.getIndicatorsCount(); j++) {
                                DashboardCache.QueryListDashboardCache.IndicatorStatistics is =
                                        qs.getIndicators(j);
                                List<AbstractQueryStatisticsResponse.RangeStat> rsr = new ArrayList<>();
                                for (int k = 0; k < is.getRangeStatCount(); k++) {
                                    DashboardCache.RangeStatCache rs = is.getRangeStat(k);
                                    rsr.add(new AbstractQueryStatisticsResponse.RangeStat(
                                            DashboardUtils.intToLocalDate(rs.getDateFrom()),
                                            DashboardUtils.intToLocalDate(rs.getDateTo()),
                                            ProtobufUtils.getDouble(rs.hasValue(), rs.getValue()),
                                            ProtobufUtils.getDouble(rs.hasDifference(), rs.getDifference())
                                    ));
                                }

                                isr.add(new AbstractQueryStatisticsResponse.IndicatorStats(
                                        QueryIndicator.R.fromValueOrUnknown(is.getQueryIndicator()),
                                        rsr
                                ));
                            }
                            result.add(new GetQueryListStatisticsResponse.QueryStat(
                                    new ViewerQuery(new QueryId(qs.getQueryId()), qs.getQueryText()),
                                    isr
                            ));
                        }
                        return Optional.of(result);
                    });
            if (cached.isPresent()) {
                log.debug("From cache: {} {} {}", request.getHostId(), dashboardType, cacheKey);
                return new GetQueryListStatisticsResponse.NormalResponse(cached.get().size(), cached.get());
            }
        } catch (Exception e) {
            log.error("Unable to read cached value: {} {} {}", request.getHostId(), dashboardType, cacheKey, e);
        }

        List<GetQueryListStatisticsResponse.QueryStat> result =
                getStatistics(request.getHostId(), specialGroup, userRangeStart, userRangeEnd, withDiff,
                        userSelectedQueryIds, indicators, regions,
                        regionInclusion, request.getDeviceType(),
                        orderBy, orderDirection, request.getPageSize(), 0);

        try {
            DashboardCache.QueryListDashboardCache.Builder qldc = DashboardCache.QueryListDashboardCache.newBuilder();
            for (GetQueryListStatisticsResponse.QueryStat queryStat : result) {
                DashboardCache.QueryListDashboardCache.QueryStatistics.Builder qs =
                        DashboardCache.QueryListDashboardCache.QueryStatistics.newBuilder();
                qs.setQueryId(queryStat.getQuery().getId().getQueryId());
                qs.setQueryText(queryStat.getQuery().getText());
                for (AbstractQueryStatisticsResponse.IndicatorStats indicatorStats : queryStat.getIndicators()) {
                    DashboardCache.QueryListDashboardCache.IndicatorStatistics.Builder is =
                            DashboardCache.QueryListDashboardCache.IndicatorStatistics.newBuilder();
                    is.setQueryIndicator(indicatorStats.getName().value());
                    for (GroupsStatisticResponse.RangeStat rangeStat : indicatorStats.getRanges()) {
                        DashboardCache.RangeStatCache.Builder rs = DashboardCache.RangeStatCache.newBuilder()
                                .setDateFrom(DashboardUtils.localDateToInt(rangeStat.getDateFrom()))
                                .setDateTo(DashboardUtils.localDateToInt(rangeStat.getDateTo()));
                        if (rangeStat.getValue() != null) {
                            rs.setValue(rangeStat.getValue());
                        }
                        if (rangeStat.getDifference() != null) {
                            rs.setDifference(rangeStat.getDifference());
                        }
                        is.addRangeStat(rs);
                    }
                    qs.addIndicators(is);
                }
                qldc.addQueries(qs);
            }

            dashboardCacheService.saveData(request.getHostId(), dashboardType, cacheKey, qldc.build().toByteArray());
        } catch (Exception e) {
            log.error("Unable to update cached value: {} {} {}", request.getHostId(), dashboardType, cacheKey, e);
        }

        return new GetQueryListStatisticsResponse.NormalResponse(result.size(), result);
    }

    private static String createKey(LocalDate userRangeStart, LocalDate userRangeEnd) {
        return TimeUtils.DF_YYYYMMDD.print(userRangeStart) + ";" + TimeUtils.DF_YYYYMMDD.print(userRangeEnd);
    }

    @Required
    public void setDashboardCacheService(DashboardCacheService dashboardCacheService) {
        this.dashboardCacheService = dashboardCacheService;
    }
}
