package ru.yandex.webmaster3.viewer.http.checklist;

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

import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
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.checklist.data.SiteProblemContent;
import ru.yandex.webmaster3.core.checklist.data.SiteProblemState;
import ru.yandex.webmaster3.core.checklist.data.SiteProblemTypeEnum;
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.util.WwwUtil;
import ru.yandex.webmaster3.storage.checklist.dao.ChecklistPageSamplesService;
import ru.yandex.webmaster3.storage.checklist.dao.ChecklistSamplesType;
import ru.yandex.webmaster3.storage.checklist.data.AbstractProblemInfo;
import ru.yandex.webmaster3.storage.checklist.data.ExtendedProblem;
import ru.yandex.webmaster3.storage.checklist.service.SiteProblemsService;
import ru.yandex.webmaster3.storage.host.service.MirrorService2;
import ru.yandex.webmaster3.storage.turbo.service.TurboSearchUrlsStatisticsService;
import ru.yandex.webmaster3.storage.turbo.service.TurboSearchUrlsStatisticsService.TurboDomainStats;
import ru.yandex.webmaster3.viewer.http.AbstractUserVerifiedHostAction;
import ru.yandex.webmaster3.viewer.http.common.response.HostHealth;

/**
 * @author avhaliullin
 */
@ReadAction
@Description("Информация о проблемах на сайте")
@Category("checklist")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Component("/checklist/info")
public class ChecklistInfoAction extends AbstractUserVerifiedHostAction<ChecklistInfoRequest, ChecklistInfoResponse> {

    // у этих проблем всегда есть примеры
    private static final List<SiteProblemTypeEnum> PROBLEMS_WITH_SAMPLES = Arrays.asList(
            SiteProblemTypeEnum.TOO_MANY_URL_DUPLICATES, SiteProblemTypeEnum.TOO_MANY_BROKEN_LINKS,
            SiteProblemTypeEnum.NOT_MOBILE_FRIENDLY, SiteProblemTypeEnum.NO_CHATS,
            SiteProblemTypeEnum.SSL_CERTIFICATE_ERROR, SiteProblemTypeEnum.TURBO_LISTING_ERROR, SiteProblemTypeEnum.DOCUMENTS_MISSING_TITLE,
            SiteProblemTypeEnum.DOCUMENTS_MISSING_DESCRIPTION, SiteProblemTypeEnum.SLOW_AVG_RESPONSE_WITH_EXAMPLES,
            SiteProblemTypeEnum.URL_ALERT_4XX, SiteProblemTypeEnum.URL_ALERT_5XX);

    private final MirrorService2 mirrorService2;
    private final ChecklistPageSamplesService checklistPageSamplesService;
    private final SiteProblemsService siteProblemsService;
    private final TurboSearchUrlsStatisticsService turboSearchUrlsStatisticsService;

    @Override
    public ChecklistInfoResponse process(ChecklistInfoRequest request) {
        WebmasterHostId hostId = request.getHostId();
        Set<SiteProblemTypeEnum> problemTypes = CollectionUtils.isEmpty(request.getTypes())
                ? SiteProblemTypeEnum.ENABLED_PROBLEMS
                : SiteProblemTypeEnum.fromStrings(request.getTypes());

        // чудо-костыль для турбо проблем магазинов и контентных сайтов
        boolean turboShop = request.getSection() == ChecklistInfoRequest.ChecklistSection.SHOP;
        boolean turboContent = request.getSection() == ChecklistInfoRequest.ChecklistSection.CONTENT;
        TurboDomainStats domainStats;
        if (CollectionUtils.containsAny(problemTypes, SiteProblemTypeEnum.TURBO_PROBLEMS) || turboShop || turboContent) {
            domainStats = turboSearchUrlsStatisticsService.getTurboInfo(WwwUtil.cutWWWAndM(hostId));
        } else {
            domainStats = null;
        }

        List<AbstractProblemInfo> problemInfos = siteProblemsService.listProblemsForHost(hostId, null, problemTypes);
        Map<SiteProblemTypeEnum, AbstractProblemInfo> problemInfoMap = problemInfos.stream()
                .collect(Collectors.toMap(AbstractProblemInfo::getProblemType, Function.identity()));

        List<AbstractProblemInfo> allProblemsInfo = new ArrayList<>();
        for (SiteProblemTypeEnum problemType : problemTypes) {
            AbstractProblemInfo info = problemInfoMap.get(problemType);
            if (info != null) {
                allProblemsInfo.add(info);
            } else {
                allProblemsInfo.add(new AbstractProblemInfo(hostId, problemType));
            }
        }

        // Здесь гарантируется, что типы проблем в extendedProblems те же, что и в allProblemsInfo,
        // то есть размеры списков одинаковы
        List<ExtendedProblem<AbstractProblemInfo>> extendedProblems =
                siteProblemsService.getExtendedProblems(hostId, allProblemsInfo);

        HostHealth health = HostHealth.fromProblemsList(extendedProblems);
        Set<SiteProblemTypeEnum> existedSamplesTypes = checklistPageSamplesService.getExistedSamplesTypes(hostId)
                .stream()
                .map(ChecklistSamplesType::getProblemType)
                .collect(Collectors.toSet());
        existedSamplesTypes.addAll(PROBLEMS_WITH_SAMPLES);

        // Отфильтруем все проблемы, неприменимые для данного хоста
        boolean isMainMirror = mirrorService2.isMainMirror(hostId);
        Map<SiteProblemTypeEnum, ExtendedProblem<AbstractProblemInfo>> problemsMap = extendedProblems.stream()
                .filter(ep -> ep.getProblem().getUserVisibleState(isMainMirror) != SiteProblemState.NOT_APPLICABLE)
                .collect(Collectors.toMap(ep -> ep.getProblem().getProblemType(), Function.identity()));

        List<ChecklistInfoResponse.ProblemInfo> problemsView = new ArrayList<>();

        int turboShopProblemsCount = 0;
        int turboContentProblemsCount = 0;

        for (SiteProblemTypeEnum problemType : problemsMap.keySet()) {
            ExtendedProblem<AbstractProblemInfo> problem = problemsMap.get(problemType);
            AbstractProblemInfo problemInfo = problem.getProblem();
            SiteProblemState state = problemInfo.getState();
            boolean present = state.isPresent();
            // reset turbo problems if needed
            if (state == SiteProblemState.PRESENT && domainStats != null) {
                boolean common = SiteProblemTypeEnum.TURBO_COMMON_PROBLEMS.contains(problemType);
                if (problemType.isTurboContentProblem(domainStats.getTurboContentSearchUrls() > 0)) {
                    turboContentProblemsCount++;
                } else if (turboContent && common) {
                    state = SiteProblemState.ABSENT;
                }
                if (problemType.isTurboShopProblem(domainStats.getTurboShopSearchUrls() > 0)) {
                    turboShopProblemsCount++;
                } else if (turboShop && common) {
                    state = SiteProblemState.ABSENT;
                }
            }

            SiteProblemContent content = present ? problemInfo.getContent() : null;
            boolean isRecheckable = problemType.isRecheckable(state, content);
            boolean hasSamples = ((content != null && content.hasSamples()) || existedSamplesTypes.contains(problemType)) && present;
            problemsView.add(new ChecklistInfoResponse.ProblemInfo(problemType, content,
                    state, problem.getWeight(), isRecheckable, problem.isRecheckRequested(),
                    problem.getRecheckRequestDate(), problemInfo.getActualSince(),
                    hasSamples, problem.isRecheckFailed()));
        }

        return new ChecklistInfoResponse(health, problemsView, turboContentProblemsCount, turboShopProblemsCount);
    }
}
