package ru.yandex.webmaster3.viewer.http.searchurl.urls;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.joda.time.Instant;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import ru.yandex.autodoc.common.doc.annotation.Description;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.http.Action;
import ru.yandex.webmaster3.core.http.ReadAction;
import ru.yandex.webmaster3.core.http.UserContext;
import ru.yandex.webmaster3.core.link.DatePoint;
import ru.yandex.webmaster3.core.metrics.Category;
import ru.yandex.webmaster3.core.sitestructure.SiteTreeNode;
import ru.yandex.webmaster3.core.util.TimeUtils;
import ru.yandex.webmaster3.storage.searchurl.history.SearchUrlHistoryService;
import ru.yandex.webmaster3.storage.searchurl.history.data.SearchUrlHistoryIndicator;
import ru.yandex.webmaster3.storage.searchurl.history.data.SearchUrlHistoryPoint;
import ru.yandex.webmaster3.storage.sitestructure.SiteStructureService;

/**
 * @author avhaliullin
 */
@Description("Получить графики истории страниц в поиске")
@ReadAction
@Category("searchurl")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class SearchUrlHistoryAction extends Action<SearchUrlHistoryRequest, SearchUrlHistoryResponse> {
    private static final Logger log = LoggerFactory.getLogger(SearchUrlHistoryAction.class);

    private static final int EXPAND_SUBNODES = 5;

    private final SearchUrlHistoryService searchUrlHistoryService;
    private final SiteStructureService siteStructureService;

    @Override
    public SearchUrlHistoryResponse process(SearchUrlHistoryRequest request) throws WebmasterException {
        List<Long> childNodes = siteStructureService.getSiteTreeNodes(request.getHostId()).getLeft().stream()
                    .filter(node -> node.getParentId() != null && node.getParentId().equals(request.getNodeId()))
                    .map(SiteTreeNode::getNodeId)
                    .limit(EXPAND_SUBNODES)
                    .collect(Collectors.toList());
        Instant dateFrom = request.getDateFrom();
        Instant dateTo = request.getDateTo();
        Set<Long> nodesToSelect = new HashSet<>(childNodes);
        nodesToSelect.add(request.getNodeId());
        UserContext.setUserId(request.getUserId());
        NavigableMap<Instant, SearchUrlHistoryPoint> rawHistory = searchUrlHistoryService.getSearchHistory(
                request.getHostId(),
                nodesToSelect,
                EnumSet.of(SearchUrlHistoryIndicator.COUNT), dateFrom, dateTo
        );
        Map<Long, NavigableMap<LocalDate, Long>> node2History = new HashMap<>();
        TreeMap<LocalDate, Long> otherHistory = new TreeMap<>();
        boolean haveNonEmptyOther = false;
        for (Map.Entry<Instant, SearchUrlHistoryPoint> entry : rawHistory.entrySet()) {
            LocalDate historyDate = entry.getKey().toDateTime(TimeUtils.EUROPE_MOSCOW_ZONE).toLocalDate();
            SearchUrlHistoryPoint point = entry.getValue();
            long otherValue = point.getValue(SearchUrlHistoryIndicator.COUNT, request.getNodeId());
            for (long childNode : childNodes) {
                long childValue = point.getValue(SearchUrlHistoryIndicator.COUNT, childNode);
                node2History.computeIfAbsent(childNode, ign -> new TreeMap<>())
                        .put(historyDate, childValue);
                otherValue -= childValue;
            }
            if (otherValue < 0) {
                log.warn("Search urls count for node " + request.getNodeId() + " is less than sum of child nodes urls count");
                otherValue = 0;
            }
            if (otherValue > 0) {
                haveNonEmptyOther = true;
            }
            otherHistory.put(historyDate, otherValue);
        }

        List<SearchUrlHistoryResponse.SearchUrlHistory> result = new ArrayList<>();
        for (long nodeId : childNodes) {
            result.add(new SearchUrlHistoryResponse.SearchUrlHistory(
                    node2History.getOrDefault(nodeId, Collections.emptyNavigableMap()).entrySet().stream()
                            .map(DatePoint::fromEntry).collect(Collectors.toList()),
                    nodeId
            ));
        }
        if (haveNonEmptyOther) {
            // Если выбрана бездетная нода, то вместо other мы будем показывать просто ее саму
            Long otherNodeId = childNodes.isEmpty() ? request.getNodeId() : null;
            result.add(new SearchUrlHistoryResponse.SearchUrlHistory(
                    otherHistory.entrySet().stream().map(DatePoint::fromEntry).collect(Collectors.toList()),
                    otherNodeId
            ));
        }
        return new SearchUrlHistoryResponse(result);
    }

}
