package ru.yandex.webmaster3.storage.searchurl.history;

import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.DateTime;
import org.joda.time.Instant;
import org.springframework.beans.factory.annotation.Autowired;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.http.UserContext;
import ru.yandex.webmaster3.core.searchbase.SearchBaseDates;
import ru.yandex.webmaster3.core.searchbase.SearchBaseUpdateInfo;
import ru.yandex.webmaster3.core.sitestructure.SearchUrlStatusUtil;
import ru.yandex.webmaster3.core.sitestructure.SiteTreeNode;
import ru.yandex.webmaster3.core.util.W3Collectors;
import ru.yandex.webmaster3.storage.abt.AbtService;
import ru.yandex.webmaster3.storage.abt.model.Experiment;
import ru.yandex.webmaster3.storage.searchbase.SearchBaseUpdatesService;
import ru.yandex.webmaster3.storage.searchurl.history.dao.SiteStructureCHDao;
import ru.yandex.webmaster3.storage.searchurl.history.dao.SiteStructureCHDao.SiteStructureRecord;
import ru.yandex.webmaster3.storage.searchurl.history.data.ExcludedUrlHistoryPoint;
import ru.yandex.webmaster3.storage.searchurl.history.data.SearchUrlHistoryIndicator;
import ru.yandex.webmaster3.storage.searchurl.history.data.SearchUrlHistoryPoint;
import ru.yandex.webmaster3.storage.searchurl.samples.dao.SearchUrlFreshStatisticsCHDao;

/**
 * @author avhaliullin
 */
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class SearchUrlHistoryService {

    private final AbtService abtService;
    private final SiteStructureCHDao siteStructureCHDao;
    private final SearchBaseUpdatesService searchBaseUpdatesService;
    private final SearchUrlFreshHistoryService searchUrlFreshHistoryService;

    public NavigableMap<Instant, SearchUrlHistoryPoint> getSearchHistory(
            WebmasterHostId hostId, Set<Long> nodes, Set<SearchUrlHistoryIndicator> indicators, Instant fromDate, Instant toDate) {
        SearchBaseDates searchBaseDates = searchBaseUpdatesService.getSearchBaseUpdates();
        NavigableMap<Instant, SearchBaseUpdateInfo> cd2Info = searchBaseDates.getCollectionDate2Info();
        toDate = ObjectUtils.min(toDate, searchBaseDates.getCurrentBase().getBaseSwitchDate());
        List<SiteStructureRecord> siteStructures = siteStructureCHDao.getSearchIndicators(hostId, fromDate, toDate, nodes);
        TreeMap<Instant, SearchUrlHistoryPoint> result = new TreeMap<>();
        for (SiteStructureRecord record : siteStructures) {
            SearchBaseUpdateInfo updateInfo = cd2Info.get(record.getTimestamp());
            if (updateInfo == null) {
                continue;
            }
            Map<SearchUrlHistoryIndicator, Long> indicatorsMap = new EnumMap<>(SearchUrlHistoryIndicator.class);
            if (indicators.contains(SearchUrlHistoryIndicator.COUNT)) {
                indicatorsMap.put(SearchUrlHistoryIndicator.COUNT, record.getNumOfDocsOnSearch());
            }
            if (indicators.contains(SearchUrlHistoryIndicator.NEW)) {
                indicatorsMap.put(SearchUrlHistoryIndicator.NEW, record.getNumOfNewSearchDocs());
            }
            if (indicators.contains(SearchUrlHistoryIndicator.GONE)) {
                indicatorsMap.put(SearchUrlHistoryIndicator.GONE, record.getNumOfGoneSearchDocs());
            }
            result.computeIfAbsent(updateInfo.getBaseSwitchDate(), (k) -> new SearchUrlHistoryPoint(new HashMap<>()))
                    .getNode2Values().put(record.getNodeId(), indicatorsMap);
        }

        if (abtService.isInExperiment(UserContext.currentUserId(), Experiment.SEARCH_URL_FRESH)) {
            if (!siteStructures.isEmpty()) {
                Instant startDate = searchBaseDates.getCurrentBase().getBaseSwitchDate();
                Instant endDate = ObjectUtils.min(toDate, DateTime.now().toInstant());
                List<SearchUrlFreshStatisticsCHDao.Record> list = searchUrlFreshHistoryService.loadFreshCount(hostId, startDate, endDate);
                for (SearchUrlFreshStatisticsCHDao.Record record : list) {
                    Map.Entry<Instant, SearchUrlHistoryPoint> entry = result.floorEntry(record.getDay());
                    // update entry with fresh data (only root node)
                    if (entry == null || indicators == null) {
                        continue;
                    }
                    Map<SearchUrlHistoryIndicator, Long> indicatorMap = entry.getValue().getNode2Values().get(SiteTreeNode.ROOT_NODE_ID);
                    if (indicatorMap == null) {
                        continue;
                    }
                    if (indicators.contains(SearchUrlHistoryIndicator.COUNT)) {
                        indicatorMap.computeIfPresent(SearchUrlHistoryIndicator.COUNT, (k, v) -> v - record.getGoneUrlsCount() + record.getNewUrlsCount());
                    }
                    if (indicators.contains(SearchUrlHistoryIndicator.NEW)) {
                        indicatorMap.computeIfPresent(SearchUrlHistoryIndicator.NEW, (k, v) -> v + record.getNewUrlsCount());
                    }
                    if (indicators.contains(SearchUrlHistoryIndicator.GONE)) {
                        indicatorMap.computeIfPresent(SearchUrlHistoryIndicator.GONE, (k, v) -> v + record.getGoneUrlsCount());
                    }
                    entry.getValue().getNode2Values().put(SiteTreeNode.ROOT_NODE_ID, indicatorMap);
                }
            }
        }
        return result;
    }

    public NavigableMap<Instant, ExcludedUrlHistoryPoint> getExcludedHistory(WebmasterHostId hostId, long nodeId, Instant fromDate, Instant toDate) {
        SearchBaseDates searchBaseDates = searchBaseUpdatesService.getSearchBaseUpdates();
        NavigableMap<Instant, SearchBaseUpdateInfo> cd2Info = searchBaseDates.getCollectionDate2Info();
        toDate = ObjectUtils.min(toDate, searchBaseDates.getCurrentBase().getBaseSwitchDate());
        List<SiteStructureRecord> siteStructures = siteStructureCHDao.getSearchStatuses(hostId, fromDate, toDate, Collections.singleton(nodeId));
        TreeMap<Instant, ExcludedUrlHistoryPoint> result = new TreeMap<>();
        for (SiteStructureRecord record : siteStructures) {
            SearchBaseUpdateInfo updateInfo = cd2Info.get(record.getTimestamp());
            if (updateInfo == null) {
                continue;
            }
            var statuses = record.getExcludedStatuses().entrySet().stream()
                    .map(entry -> Pair.of(SearchUrlStatusUtil.raw2View(entry.getKey(), false), entry.getValue()))
                    .collect(W3Collectors.toHashMap(Long::sum));
            result.put(cd2Info.get(record.getTimestamp()).getBaseSwitchDate(), new ExcludedUrlHistoryPoint(statuses));
        }
        return result;
    }

}
