package ru.yandex.webmaster3.viewer.http.niche2;

import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import ru.yandex.autodoc.common.doc.annotation.Category;
import ru.yandex.autodoc.common.doc.annotation.Description;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.http.ActionResponse;
import ru.yandex.webmaster3.core.http.ReadAction;
import ru.yandex.webmaster3.core.http.RequestQueryProperty;
import ru.yandex.webmaster3.core.util.FNVHash;
import ru.yandex.webmaster3.storage.iks.IksService;
import ru.yandex.webmaster3.storage.niche2.Niche2RivalsCHDao;
import ru.yandex.webmaster3.storage.niche2.Niche2RivalsCHDao.ClusterPopularityHistory;
import ru.yandex.webmaster3.storage.niche2.Niche2RivalsCHDao.PopularityRecord;
import ru.yandex.webmaster3.viewer.http.AbstractUserVerifiedHostAction;
import ru.yandex.webmaster3.viewer.http.host.verification.UsersVerifiedHostRequest;

import static java.lang.Math.min;

@ReadAction
@Category("niche2")
@Component("/niche2/visibilityReport")
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class GetNiche2VisibilityReportAction extends AbstractUserVerifiedHostAction<GetNiche2VisibilityReportAction.Request,
        GetNiche2VisibilityReportAction.Response> {
    private final Niche2RivalsCHDao niche2RivalsCHDao;
    private final IksService iksService;

    private static final int RIVAL_EXAMPLES_SIZE = 3;

    @Override
    public Response process(Request request) {
        var p = request.p;
        var pSize = request.pSize;
        var dateFrom = request.dateFrom.toLocalDate();
        var dateTo = request.dateTo.toLocalDate();
//        график видимости
        var hostChart = niche2RivalsCHDao.getHostVisibilityInfo(request.getHostId(),
                        request.groupId,
                        dateFrom,
                        dateTo,
                        request.regionIds)
                .stream()
                .map(x ->
                        x.history().stream().map(record ->
                                new HistoryPoint(record.date(), record.visibility())).collect(Collectors.toList()))
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
//        получаем примеры хостов из кластеров для таблицы под графиком
        var rivalsExamplesList = niche2RivalsCHDao.getRivalsExamples(request.getHostId(),
                request.groupId,
                request.regionIds);
//        получаем ИКСы
        var allHosts = rivalsExamplesList.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
        allHosts.add(request.getHostId());
        var host2sqi = iksService.getIksValues(allHosts);
//        получаем строку про хост в таблице под графиком
        var hostVisibilityInfo = new HostInfo(hostChart,
                new HostSqi(request.getHostId(), host2sqi.get(request.getHostId())),
                hostChart
                        .stream()
                        .max(Comparator.comparing(HistoryPoint::getDate))
                        .map(x -> x.currentVisibility)
                        .orElse(0.));
//        получаем строки про конкурентов в таблице под графиком
        var rivalsList = niche2RivalsCHDao.getRivalsVisibilityInfo(request.getHostId(),
                request.groupId,
                request.regionIds);
//        делаем непосредственно данные для таблицы
        var rivals =
                rivalsList
                        .stream()
                        .map(RivalInfo::new)
                        .filter(x -> rivalsExamplesList.containsKey(x.clusterName))
                        .map(x -> x.AddedExamples(rivalsExamplesList.get(x.clusterName).stream().map(hostId -> new HostSqi(hostId,
                                host2sqi.get(hostId))).collect(Collectors.toList())))
                        .sorted((x, y) -> y.currentVisibility.compareTo(x.currentVisibility))
                        .collect(Collectors.toList());
        return new Response(
                hostVisibilityInfo,
                rivals.subList(min(rivals.size(), p * pSize), min(rivals.size(), (p + 1) * pSize)),
                rivals.size()
        );
    }

    public static class Request extends UsersVerifiedHostRequest {
        @Setter(onMethod_ = {@Description("Начало периода графика"), @RequestQueryProperty(required = true)})
        DateTime dateFrom;
        @Setter(onMethod_ = {@Description("Конец периода графика"), @RequestQueryProperty(required = true)})
        DateTime dateTo;
        @Setter(onMethod_ = {@Description("Идентификатор группы"), @RequestQueryProperty(required = true)})
        String groupId;
        @Setter(onMethod_ = {@Description("Номер страницы таблицы под графиком"), @RequestQueryProperty})
        int p = 0;
        @Setter(onMethod_ = {@Description("Кол-во позиций на 1 странице под графиком"),
                @RequestQueryProperty})
        int pSize = 20;
        @Setter(onMethod_ = {@Description("Фильтр по регионам"), @RequestQueryProperty})
        Integer[] regionIds;
    }

    @Value
    @AllArgsConstructor
    public static class RivalInfo {
        @Description("Название кластера")
        String clusterName;
        Double currentVisibility;
        List<HostSqi> rivals;

        public RivalInfo(ClusterPopularityHistory clusterPopularityHistory) {
            clusterName = clusterPopularityHistory.cluster();
            if (clusterPopularityHistory.history().isEmpty()) {
                currentVisibility = 0.;
            } else {
                currentVisibility =
                        clusterPopularityHistory
                                .history()
                                .stream()
                                .mapToDouble(PopularityRecord::visibility)
                                .sum() / clusterPopularityHistory.history().size();
            }
            rivals = List.of();
        }

        public RivalInfo AddedExamples(List<HostSqi> hostSqis) {
            return new RivalInfo(clusterName, currentVisibility, Sample(hostSqis));
        }
    }

    @Value
    @AllArgsConstructor
    public static class HostSqi {
        WebmasterHostId hostId;
        Integer sqi;
    }

    @Value
    @AllArgsConstructor
    public static class HostInfo {
        @Description("Точки хоста для графика")
        List<HistoryPoint> visibilityHistory;
        HostSqi sqi;
        Double currentVisibility;
    }

    @Value
    public static class HistoryPoint {
        LocalDate date;
        Double currentVisibility;

        public HistoryPoint(LocalDate date, double currentVisibility) {
            this.date = date;
            this.currentVisibility = currentVisibility;
        }
    }

    @Value
    @AllArgsConstructor
    public static class HostHistory {
        WebmasterHostId hostId;
        List<HistoryPoint> historyPoints;
    }

    private static List<HostSqi> Sample(List<HostSqi> list) {
        return list
                .stream()
                .sorted(Comparator.comparing(x -> FNVHash.hash64(x.hostId.toStringId())))
                .limit(RIVAL_EXAMPLES_SIZE)
                .collect(Collectors.toList());
    }

    @Value
    public static class Response implements ActionResponse.NormalResponse {
        @Description("Информация про пользователя")
        HostInfo host;
        @Description("Список конкурентов в виде кластеров для данного хоста")
        List<RivalInfo> rivals;
        @Description("Количество конкурентов(включены только кластеры)")
        Integer rivalsAmount;
    }
}
