package ru.yandex.webmaster3.storage.turbo.service;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.feeds.feed.NativeFeedInfo2;
import ru.yandex.webmaster3.core.feeds.feed.NativeFeedSccStatus;
import ru.yandex.webmaster3.core.feeds.mbi.MbiService;
import ru.yandex.webmaster3.core.feeds.mbi.RegisterFeedResponse;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboApiTaskWithResult;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboCrawlState;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedInfo;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedSettings;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedSettingsInfo;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedState;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedStatistics;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedStatisticsInfo;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedType;
import ru.yandex.webmaster3.core.util.W3CollectionUtils;
import ru.yandex.webmaster3.core.util.W3Collectors;
import ru.yandex.webmaster3.storage.feeds.FeedsNative2YDao;
import ru.yandex.webmaster3.storage.feeds.FeedsService;
import ru.yandex.webmaster3.storage.turbo.dao.TurboDomainsStateCHDao;
import ru.yandex.webmaster3.storage.turbo.dao.TurboFeedsSettingsYDao;
import ru.yandex.webmaster3.storage.turbo.dao.TurboInvalidCartDomainsYDao;
import ru.yandex.webmaster3.storage.turbo.dao.api.TurboApiHostTasksYDao;
import ru.yandex.webmaster3.storage.turbo.dao.app.TurboAppSettingsYDao;
import ru.yandex.webmaster3.storage.turbo.dao.scc.TurboSccService;
import ru.yandex.webmaster3.storage.turbo.dao.statistics.TurboFeedsStatistics2YDao;
import ru.yandex.webmaster3.storage.turbo.service.TurboDomainsStateService.TurboDomainState;

import static ru.yandex.webmaster3.storage.turbo.dao.scc.TurboSccService.FrontModerationStatus.INVALID_OUTER_CART;

/**
 * Created by Oleg Bazdyrev on 22/11/2017.
 */
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class TurboFeedsService {

    private final TurboApiHostTasksYDao turboApiHostTasksYDao;
    private final TurboDomainsStateCHDao turboDomainsStateCHDao;
    private final TurboFeedsSettingsYDao turboFeedsSettingsYDao;
    private final TurboFeedsStatistics2YDao turboFeedsStatistics2YDao;
    private final TurboSccService turboSccService;
    private final TurboAppSettingsYDao turboAppSettingsYDao;
    private final TurboInvalidCartDomainsYDao turboInvalidCartDomainsYDao;
    private final MbiService mbiService;
    private final FeedsService feedsService;
    private final FeedsNative2YDao feedsNative2YDao;

    public void deleteFeed(TurboFeedSettings feed) {
        if (feed.getType().isYml() && feed.getPartnerId() != null) {
            NativeFeedInfo2 nativeFeedInfo2 = feedsNative2YDao.get(feed.getDomain(), feed.getUrl());
            if (nativeFeedInfo2 == null && feed.getStatus() != NativeFeedSccStatus.BANNED) {
                mbiService.removeShop(feed.getPartnerId());
            }
        }
        turboFeedsSettingsYDao.deleteFeed(feed);
    }

    public void insertFeed(TurboFeedSettings feed) {
        insertFeed(feed, null);
    }

    public void updateScc(TurboFeedSettings feed) {
        turboFeedsSettingsYDao.updateScc(feed);
    }


    public void insertFeed(TurboFeedSettings newFeedState, Long userId) {
        var finalFeed = newFeedState;
        final List<TurboFeedSettings> feeds = turboFeedsSettingsYDao.getFeeds(newFeedState.getDomain());
        // vertical_share info
        NativeFeedInfo2 nativeFeedInfo2 = feedsNative2YDao.get(newFeedState.getDomain(), newFeedState.getUrl());

        // ветка добавления фидв в mbi
        if (newFeedState.getType().isYml()) {
            if (nativeFeedInfo2 == null || !nativeFeedInfo2.isMbiFeed()) {
                Optional<TurboFeedSettings> prevFeedState = feeds.stream().filter(f -> f.equals(finalFeed)).findFirst();

                // включили этот фид
                if ((prevFeedState.isEmpty() || !prevFeedState.get().isActive()) && newFeedState.isActive()) {
                    Preconditions.checkState(userId != null, "userId for add not null");
                    newFeedState = registerMbiFeed(newFeedState, userId);
                    // выключили фид
                } else if (prevFeedState.isPresent() &&
                        prevFeedState.get().isActive() &&
                        !newFeedState.isActive() &&
                        newFeedState.getPartnerId() != null) {
                    if (newFeedState.getStatus() == NativeFeedSccStatus.IN_PROGRESS) {
                        newFeedState = newFeedState.toBuilder()
                                .status(NativeFeedSccStatus.UNKNOWN)
                                .build();
                    }
                    if (newFeedState.getStatus() != NativeFeedSccStatus.BANNED) {
                        mbiService.removeShop(newFeedState.getPartnerId());
                    }
                }
                // проброс статус скк из ТВ фида
            } else if (newFeedState.getPartnerId() == null && nativeFeedInfo2.isMbiFeed()) {
                String errorScc = String.join("\n", nativeFeedInfo2.getErrorsScc());
                newFeedState = newFeedState.toBuilder()
                        .businessId(nativeFeedInfo2.getBusinessId())
                        .partnerId(nativeFeedInfo2.getPartnerId())
                        .feedId(nativeFeedInfo2.getFeedId())
                        .status(nativeFeedInfo2.getStatusScc())
                        .errorScc(errorScc)
                        .timeScc(DateTime.now())
                        .build();
            }

            // т.е. мы не можем добавить или обновить фид, который yml, активирован и при этом скк == BANNED

            final TurboSccService.FrontModerationStatus sccStatus =
                    turboInvalidCartDomainsYDao.isInvalidCart(newFeedState.getDomain())
                    ? INVALID_OUTER_CART
                    : turboSccService.getFrontModerationStatus(newFeedState.getDomain());

            //todo разобраться возможно Устаревшее
            //WMC-10146: вводим костыльный статус, который обозначает что пользователю нужно показатьплохой статус
            if ((sccStatus == INVALID_OUTER_CART)
                    && newFeedState.isActive()) {
                turboInvalidCartDomainsYDao.delete(newFeedState.getDomain());
            }
        }


        turboFeedsSettingsYDao.insertFeed(newFeedState);
    }

    private TurboFeedSettings registerMbiFeed(TurboFeedSettings feed, long userId) {
        String domain = feed.getDomain();
        String url = feed.getUrl();
        RegisterFeedResponse resp = feedsService.registerMbiFeed(domain, url, null, userId, null, null);
        return feed.toBuilder()
                .businessId(resp.getBusinessId())
                .partnerId(resp.getPartnerId())
                .feedId(resp.getFeedId())
                .status(NativeFeedSccStatus.IN_PROGRESS)
                .build();
    }


    public List<TurboFeedSettings> getFeeds(String domain) {
        return turboFeedsSettingsYDao.getFeeds(domain);
    }

    public void disableYmlFeeds(String domain) {
        getFeeds(domain).stream()
                .filter(feed -> feed.isActive() && feed.getType().isYml())
                .map(feed -> feed.activate(false))
                .forEach(this::insertFeed);
    }

    public List<TurboFeedSettings> getFeeds(Collection<String> domains) {
        return turboFeedsSettingsYDao.getFeeds(domains);
    }

    public TurboFeedSettings getFeed(String domain, String url) {
        return turboFeedsSettingsYDao.getFeed(domain, url);
    }

    /**
     * Находит настройки фида вместе с актуальной статистикой или null - если такого фида нет
     *
     * @param domain
     * @param feedUrl
     * @return
     * @
     */
    public Pair<TurboFeedSettings, TurboFeedStatistics> getFeedWithStatistics(String domain, String feedUrl) {
        TurboFeedSettings settings = getFeed(domain, feedUrl);
        if (settings == null || settings.getState() == TurboFeedState.DELETED) {
            return null;
        }
        // ищем новую статистику
        TurboFeedStatistics statistics;
        if (settings.isActive()) {
            statistics = turboDomainsStateCHDao.getFeedStatistics(domain, feedUrl);
        } else {
            statistics = turboFeedsStatistics2YDao.getStatistics(feedUrl, settings.isActive());
        }
        return Pair.of(settings, statistics);
    }

    public Map<TurboFeedSettings, TurboFeedStatistics> getFeedsWithStatistics(String domain) {
        return getFeedsWithStatistics(domain, null, null);
    }

    public Map<TurboFeedSettings, TurboFeedStatistics> getFeedsWithStatistics(String domain, Collection<String> feedUrls) {
        return getFeedsWithStatistics(domain, null, feedUrls);
    }

    /**
     * Находит все фиды с актуальной статистикой
     *
     * @param domain
     * @return
     * @
     */
    public Map<TurboFeedSettings, TurboFeedStatistics> getFeedsWithStatistics(String domain, TurboDomainState domainState, Collection<String> feedUrls) {
        List<TurboFeedSettings> settingsList = getFeeds(domain).stream()
                .filter(tfs -> CollectionUtils.isEmpty(feedUrls) || feedUrls.contains(tfs.getUrl()))
                .collect(Collectors.toList());
        Set<String> allFeedUrls = settingsList.stream().map(TurboFeedSettings::getUrl).collect(Collectors.toSet());
        if (allFeedUrls.isEmpty()) {
            return Collections.emptyMap();
        }
        // статистика
        Map<Pair<String, Boolean>, TurboFeedStatistics> allFeeds = turboFeedsStatistics2YDao.getStatistics(allFeedUrls);
        if (domainState == null) {
            domainState = turboDomainsStateCHDao.getDomainStateOnlyFeeds(domain);
        }
        Map<String, TurboFeedStatistics> activeFeeds = Stream.concat(domainState.getRssFeeds().stream(), domainState.getYmlFeeds().stream())
                .collect(Collectors.toMap(TurboFeedStatistics::getUrl, Function.identity(), W3Collectors.replacingMerger()));

        Map<TurboFeedSettings, TurboFeedStatistics> result = new HashMap<>();
        for (TurboFeedSettings settings : settingsList) {
            boolean active = settings.isActive();
            String url = settings.getUrl();
            result.put(settings, active ? activeFeeds.get(url) : allFeeds.get(Pair.of(url, false)));
        }
        return result;
    }

    /**
     * Получение инфы по "фидам", загруженным через API
     *
     * @param hostId
     * @return
     */
    public TurboFeedInfo getLatestProcessedApiFeedStats(WebmasterHostId hostId) {
        TurboApiTaskWithResult taskWithResult = turboApiHostTasksYDao.getLastProcessedTask(hostId);
        Pair<TurboFeedSettingsInfo, TurboFeedStatisticsInfo> pair = getApiInfo(hostId, taskWithResult);
        if (pair != null && pair.getRight() != null) {
            return new TurboFeedInfo(pair.getLeft(), pair.getRight(), null);
        }
        return null;
    }

    /**
     * Получение инфы по "фидам", загруженным через API
     *
     * @param hostId
     * @return
     */
    public Pair<TurboFeedSettingsInfo, TurboFeedStatisticsInfo> getApiFeedStats(WebmasterHostId hostId, UUID taskId) {
        return getApiInfo(hostId, turboApiHostTasksYDao.getTask(hostId, taskId));
    }

    private Pair<TurboFeedSettingsInfo, TurboFeedStatisticsInfo> getApiInfo(WebmasterHostId hostId, TurboApiTaskWithResult task) {
        if (task == null) {
            return null;
        }
        if (task.getState() == TurboCrawlState.PROCESSING) {
            return Pair.of(task, null);
        }
        TurboApiTaskWithResult fullTask = turboApiHostTasksYDao.getFullTask(hostId, task.getTaskId());
        TurboFeedStatistics feedStatistics = new TurboFeedStatistics(
                hostId, TurboFeedType.API, null, task.isActive(), W3CollectionUtils.truncate(fullTask.getUrls(), 10), task.getImportDate(),
                task.getImportDate(), null, W3CollectionUtils.truncate(fullTask.getErrors(), 10), task.getStats(), task.getHash()
        );
        return Pair.of(task, feedStatistics);
    }
}
