package ru.yandex.webmaster3.viewer.http.turbo.feed;

import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import ru.yandex.autodoc.common.doc.annotation.Description;
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.turbo.model.feed.TurboFeedInfo;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedInfo.TurboFeedInfoState;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedSettings;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedStatistics;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedType;
import ru.yandex.webmaster3.core.turbo.model.statistics.TurboDomainStatistics;
import ru.yandex.webmaster3.core.turbo.model.statistics.TurboTotalStatistics;
import ru.yandex.webmaster3.core.turbo.model.statistics.TurboTotalStatistics.TurboTotalStatisticsBuilder;
import ru.yandex.webmaster3.core.util.WwwUtil;
import ru.yandex.webmaster3.storage.host.CommonDataType;
import ru.yandex.webmaster3.storage.settings.SettingsService;
import ru.yandex.webmaster3.storage.turbo.dao.statistics.TurboDomainStatisticsYDao;
import ru.yandex.webmaster3.storage.turbo.service.TurboFeedsService;
import ru.yandex.webmaster3.viewer.http.AbstractUserVerifiedHostAction;

/**
 * Created by Oleg Bazdyrev on 06/07/2017.
 */
@ReadAction
@Category("turbo")
@Description("Возвращает список настроенных турбо-фидов вместе с подробной информацией о них")
@Component("/turbo/feed/list")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class GetTurboFeedsAction extends AbstractUserVerifiedHostAction<GetTurboFeedsRequest, GetTurboFeedsResponse> {

    private static final DateTime MIN_DATE = new DateTime(0L);
    private static final EnumSet<TurboFeedType> SOURCE_TYPES = EnumSet.of(TurboFeedType.API, TurboFeedType.RSS, TurboFeedType.YML, TurboFeedType.AUTO);

    private final TurboFeedsService turboFeedsService;
    private final TurboDomainStatisticsYDao turboDomainStatisticsYDao;
    private final SettingsService settingsService;

    @Override
    public GetTurboFeedsResponse process(GetTurboFeedsRequest request) {
        // затягиваем настройки из кассандры
        WebmasterHostId hostId = request.getHostId();
        String domain = WwwUtil.cutWWWAndM(hostId);
        List<String> feedUrls = request.getFeedUrls();

        // обычные RSS-фида
        Map<TurboFeedSettings, TurboFeedStatistics> feedMap = turboFeedsService.getFeedsWithStatistics(domain, feedUrls);
        // последний API-фид
        TurboFeedInfo apiFeedInfo = turboFeedsService.getLatestProcessedApiFeedStats(hostId);

        // статистика по хосту
        List<TurboDomainStatistics> turboHostStatsList = turboDomainStatisticsYDao.getStatistics(domain);
        DateTime lastStatsUpdate = Optional.ofNullable(
                settingsService.getSettingOrNull(CommonDataType.TURBO_HOST_STATS_LAST_UPDATE))
                .map(cds -> new DateTime(Long.parseLong(cds.getValue()))).orElse(MIN_DATE);

        final Predicate<TurboDomainStatistics> turboDomainStatisticsPredicate = TurboFeedFilter.toPredicateTurboDomainStatistics(request.getParameterFilters());

        TurboTotalStatisticsBuilder totalStatistics = new TurboTotalStatisticsBuilder();
        // нам нужна статистика по фидам
        EnumMap<TurboFeedType, Map<String, TurboTotalStatisticsBuilder>> totalStatsByType =
                new EnumMap<>(TurboFeedType.class);
        for (TurboDomainStatistics stats : turboHostStatsList) {
            if (!SOURCE_TYPES.contains(stats.getSource())) {
                continue;
            }
            //  костыль для всей статистики - проверим дату последнего обновления
            if (stats.getUpdateDate() == null || stats.getUpdateDate().isBefore(lastStatsUpdate)) {
                continue;
            }
            if (turboDomainStatisticsPredicate.test(stats)) {
                totalStatistics.add(stats);
            }
            totalStatsByType.computeIfAbsent(stats.getSource(), k -> new HashMap<>())
                    .computeIfAbsent(stats.getFeed(), k -> new TurboTotalStatisticsBuilder()).add(stats);
        }

        Predicate<TurboFeedInfo> predicate = TurboFeedFilter.toPredicateTurboFeedInfo(request.getParameterFilters());
        // фильтруем
        List<TurboFeedInfo> feedInfos = feedMap.entrySet().stream()
                .map(entry -> new TurboFeedInfo(entry.getKey(), entry.getValue(),
                        totalStatsByType.getOrDefault(entry.getKey().getType(), Collections.emptyMap()).get(entry.getKey().getUrl())))
                .filter(predicate)
                .collect(Collectors.toList());
        int total = feedInfos.size();
        // сортируем, пагинируем
        feedInfos = feedInfos.stream().sorted(TurboFeedInfo.BY_ADD_DATE).collect(Collectors.toList());

        // API
        TurboTotalStatisticsBuilder apiTotalStatBuilder = totalStatsByType
                .getOrDefault(TurboFeedType.API, Collections.emptyMap()).get("");
        if (apiTotalStatBuilder == null) {
            apiTotalStatBuilder = new TurboTotalStatisticsBuilder();
        }
        TurboTotalStatistics apiTotalStats = apiTotalStatBuilder.build();
        apiFeedInfo = apiFeedInfo == null ? null : apiFeedInfo.withTotalStats(apiTotalStats);

        Set<TurboFeedType> availableTypes = feedInfos.stream()
                .map(TurboFeedInfo::getType).collect(Collectors.toSet());
        Set<TurboFeedInfoState> availableStates = feedInfos.stream()
                .map(TurboFeedInfo::getState).collect(Collectors.toSet());
        return new GetTurboFeedsResponse.NormalResponse(total, feedInfos, availableTypes, availableStates,
                apiFeedInfo, totalStatistics.buildWithoutDeleted());
    }
}
