package ru.yandex.webmaster3.storage.sitestructure;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.Instant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import ru.yandex.webmaster3.core.codes.DownloadedHttpCodeGroup;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.sitestructure.NewSiteStructure;
import ru.yandex.webmaster3.core.sitestructure.NewSiteStructureNode;
import ru.yandex.webmaster3.core.sitestructure.SiteTreeNode;
import ru.yandex.webmaster3.core.util.WwwUtil;
import ru.yandex.webmaster3.storage.checklist.data.SummarySiteProblemsInfo;
import ru.yandex.webmaster3.storage.host.CommonDataType;
import ru.yandex.webmaster3.storage.host.HostDataState;
import ru.yandex.webmaster3.storage.host.HostIndicatorsDiff;
import ru.yandex.webmaster3.storage.searchbase.SearchBaseUpdatesService;
import ru.yandex.webmaster3.storage.searchurl.history.dao.SiteStructureCHDao;
import ru.yandex.webmaster3.storage.settings.SettingsService;
import ru.yandex.webmaster3.storage.turbo.service.TurboSearchUrlsStatisticsService;
import ru.yandex.webmaster3.storage.turbo.service.TurboSearchUrlsStatisticsService.TurboSourceStatuses;
import ru.yandex.webmaster3.storage.user.dao.UserSiteTreeYDao;

/**
 * @author aherman
 */
@Component("siteStructureService")
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class SiteStructureService {

    private final SearchBaseUpdatesService searchBaseUpdatesService;
    private final SettingsService settingsService;
    private final SiteStructureCHDao siteStructureCHDao;
    private final TurboSearchUrlsStatisticsService turboSearchUrlsStatisticsService;
    private final UserSiteTreeYDao userSiteTreeYDao;

    public Pair<List<SiteTreeNode>, List<SiteTreeNode>> getSiteTreeNodes(WebmasterHostId hostId) {
        return getSiteTreeNodes(hostId, false);
    }

    public Pair<List<SiteTreeNode>, List<SiteTreeNode>> getSiteTreeNodes(WebmasterHostId hostId, boolean withTurboStats) {
        Instant baseDate = new Instant(settingsService.getSettingCached(CommonDataType.LAST_IMPORTED_SITE_STRUCTURES, Long::parseLong) * 1000L);
        // get records for base
        List<SiteStructureCHDao.SiteStructureRecord> records = siteStructureCHDao.getSiteStructures(hostId, baseDate, baseDate, null);

        TurboSourceStatuses turboSourceStatuses = withTurboStats ? turboSearchUrlsStatisticsService.getTurboSourceStatuses(WwwUtil.cutWWWAndM(hostId)) : null;

        List<SiteTreeNode> autoNodes = new ArrayList<>();
        List<SiteTreeNode> userNodes = new ArrayList<>();
        Map<String, SiteStructureCHDao.SiteStructureRecord> userRecordsByName = records.stream()
                .filter(SiteStructureCHDao.SiteStructureRecord::isUserNode)
                .collect(Collectors.toMap(SiteStructureCHDao.SiteStructureRecord::getName, Function.identity()));
        if (records.isEmpty()) {
            // no data for site, add empty root node
            autoNodes.add(new SiteTreeNode(SiteTreeNode.ROOT_NODE_ID, null, "/", null, null, null, null, false, null));
        } else {
            for (SiteStructureCHDao.SiteStructureRecord record : records) {
                if (!record.isUserNode()) {
                    SiteTreeNode autoNode = SiteTreeNode.builder()
                            .id(record.getNodeId())
                            .parentId(record.getParentNodeId())
                            .name(record.getName())
                            .originalName(record.getName())
                            .docCount(getDownloadedDocsCount(record.getHttpCodes()))
                            .searchDocCount(record.getNumOfDocsOnSearch())
                            .isUserNode(false)
                            .searchTurboDocs(record.getSearchTurboDocs())
                            .turboDocCount(turboSearchUrlsStatisticsService.countTurboPages(turboSourceStatuses, record.getSearchTurboDocs()))
                            .build();
                    autoNodes.add(autoNode);
                }
            }
        }
        // user nodes
        for (SiteTreeNode userNode : userSiteTreeYDao.getUserTreeNodes(hostId)) {
            SiteStructureCHDao.SiteStructureRecord record = userRecordsByName.get(userNode.getName());
            if (record == null) {
                userNodes.add(userNode);
            } else {
                // merge data
                userNode = userNode.toBuilder()
                        .docCount(getDownloadedDocsCount(record.getHttpCodes()))
                        .searchDocCount(record.getNumOfDocsOnSearch())
                        .searchTurboDocs(record.getSearchTurboDocs())
                        .turboDocCount(turboSearchUrlsStatisticsService.countTurboPages(turboSourceStatuses, record.getSearchTurboDocs()))
                        .build();
                userNodes.add(userNode);
            }
        }
        autoNodes.sort(SiteTreeNode.NODE_COMPARATOR);
        userNodes.sort(SiteTreeNode.NODE_COMPARATOR);
        return Pair.of(autoNodes, userNodes);
    }

    // TODO избавиться от NewSiteStructureNode в пользу чего-то единого
    public NewSiteStructureNode getNode(WebmasterHostId hostId, long nodeId) {
        Instant baseDate = new Instant(settingsService.getSettingCached(CommonDataType.LAST_IMPORTED_SITE_STRUCTURES, Long::parseLong) * 1000L);
        return siteStructureCHDao.getSiteStructures(hostId, baseDate, baseDate, Collections.singleton(nodeId))
                .stream().map(SiteStructureCHDao.SiteStructureRecord::toNewSiteStructureNode).findFirst().orElse(null);
    }

    // TODO temp
    public HostDataState getHostDataState(WebmasterHostId hostId) {
        Instant baseDate = new Instant(settingsService.getSettingCached(CommonDataType.LAST_IMPORTED_SITE_STRUCTURES, Long::parseLong) * 1000L);
        Instant fromDate = searchBaseUpdatesService.getSearchBaseUpdates().getCollectionDate2Info().lowerKey(baseDate);
        List<SiteStructureCHDao.SiteStructureRecord> list =
                siteStructureCHDao.getSiteStructures(hostId, fromDate, baseDate, Collections.singleton(NewSiteStructure.ROOT_NODE_ID));
        if (list.isEmpty()) {
            return null;
        }
        // convert to fake HostDataState
        HostIndicatorsDiff diff = new HostIndicatorsDiff(list.get(0).toHostIndicatorsState(), list.get(list.size() - 1).toHostIndicatorsState());
        return new HostDataState(hostId, null, null, null, null, null, null, null, new SummarySiteProblemsInfo(0, 0, Collections.emptyMap()), null, null, diff);
    }

    private long getDownloadedDocsCount(Map<Integer, Long> httpCodes) {
        return httpCodes.entrySet()
                .stream()
                .filter(e -> DownloadedHttpCodeGroup.get(e.getKey()).isPresent())
                .mapToLong(Map.Entry::getValue).sum();
    }
}
