package ru.yandex.webmaster3.worker.mobile;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;

import NSamovar.NRecord.RecordFields;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.checklist.data.MobileAuditResolution;
import ru.yandex.webmaster3.core.checklist.data.MobileUrlCheckStatus;
import ru.yandex.webmaster3.core.checklist.data.NotMobileFriendlyStatus;
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.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.logbroker.reader.IDataProcessing;
import ru.yandex.webmaster3.core.logbroker.reader.MessageContainer;
import ru.yandex.webmaster3.core.solomon.Indicators;
import ru.yandex.webmaster3.core.solomon.SolomonSensor;
import ru.yandex.webmaster3.core.solomon.metric.SolomonCounter;
import ru.yandex.webmaster3.core.solomon.metric.SolomonGroupingUtil;
import ru.yandex.webmaster3.core.solomon.metric.SolomonKey;
import ru.yandex.webmaster3.core.solomon.metric.SolomonMetricConfiguration;
import ru.yandex.webmaster3.core.solomon.metric.SolomonMetricRegistry;
import ru.yandex.webmaster3.core.util.IdUtils;
import ru.yandex.webmaster3.core.util.RetryUtils;
import ru.yandex.webmaster3.core.util.environment.YandexEnvironmentType;
import ru.yandex.webmaster3.storage.abt.AbtService;
import ru.yandex.webmaster3.storage.checklist.dao.ChecklistPageSamplesService;
import ru.yandex.webmaster3.storage.checklist.dao.ChecklistSamplesType;
import ru.yandex.webmaster3.storage.mobile.dao.HostMobileAuditQueueYDao;
import ru.yandex.webmaster3.storage.checklist.dao.SiteProblemsRecheckYDao;
import ru.yandex.webmaster3.storage.checklist.data.ProblemSignal;
import ru.yandex.webmaster3.storage.checklist.data.problems.MobileUrlSample;
import ru.yandex.webmaster3.storage.checklist.service.SiteProblemsService;
import ru.yandex.webmaster3.storage.host.AllVerifiedHostsCacheService;
import ru.yandex.webmaster3.storage.host.service.MirrorService2;
import ru.yandex.webmaster3.storage.mobile.dao.HostMobileAuditResultsOverrideYDao;
import ru.yandex.webmaster3.storage.util.JsonDBMapping;

import static NSamovar.NRecord.RecordFields.TMobileCheckProto.*;

/**
 * @author leonidrom
 */
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class HostMobileAuditResultsProcessingService implements IDataProcessing {
    private static final int MAX_TIMEOUT_RETRIES = 3;
    private static final Pattern KEY_PATTERN = Pattern.compile("^(?:[A-Z]+@)?(.+)$");
    private static final RetryUtils.RetryPolicy RETRY_POLICY = RetryUtils.linearBackoff(5, Duration.standardSeconds(30));

    private final SiteProblemsService siteProblemsService;
    private final HostMobileAuditQueueYDao hostMobileAuditQueueYDao;
    @Value("${webmaster3.environmentType}")
    private final YandexEnvironmentType yandexEnvironmentType;
    private final SolomonMetricRegistry solomonMetricRegistry;
    private final AllVerifiedHostsCacheService allVerifiedHostsCacheService;
    private final MirrorService2 mirrorService2;
    private final SiteProblemsRecheckYDao siteProblemsRecheckYDao;
    private final ChecklistPageSamplesService checklistPageSamplesService;
    private final HostMobileAuditResultsOverrideYDao hostMobileAuditResultsOverrideYDao;
    private final AbtService abtService;

    private boolean enableLogging = false;
    private SolomonMetricConfiguration solomonMetricConfiguration;
    private Map<Pair<String, String>, SolomonCounter> solomonMetricMap;

    @PostConstruct
    public void init() {
        enableLogging = yandexEnvironmentType == YandexEnvironmentType.PRODUCTION;
        solomonMetricConfiguration = new SolomonMetricConfiguration();
        solomonMetricMap = new HashMap<>();
        for (var source : EHostCheckSource.values()) {
            String sourceStr = source.toString();
            solomonMetricMap.put(Pair.of(sourceStr, SolomonGroupingUtil.AGGREGATE_LABEL_VALUE),
                    createSolomonCounter(sourceStr, SolomonGroupingUtil.AGGREGATE_LABEL_VALUE));

            for (String reasonStr : getReasons()) {
                solomonMetricMap.put(Pair.of(sourceStr, reasonStr),
                        createSolomonCounter(sourceStr, reasonStr));
                solomonMetricMap.put(Pair.of(SolomonGroupingUtil.AGGREGATE_LABEL_VALUE, reasonStr),
                        createSolomonCounter(SolomonGroupingUtil.AGGREGATE_LABEL_VALUE, reasonStr));
            }
        }

        solomonMetricMap.put(Pair.of(SolomonGroupingUtil.AGGREGATE_LABEL_VALUE, SolomonGroupingUtil.AGGREGATE_LABEL_VALUE),
                createSolomonCounter(SolomonGroupingUtil.AGGREGATE_LABEL_VALUE, SolomonGroupingUtil.AGGREGATE_LABEL_VALUE));
    }

    @Override
    public void process(MessageContainer messageContainer) {
        List<byte[]> rawMessages = messageContainer.getRawMessages();
        if (enableLogging) {
            log.info("Total messages: {}", rawMessages.size());
        }

        for (byte[] rawMessage : rawMessages) {
            try {
                if (enableLogging) {
                    log.info("Message length: {}", rawMessage.length);
                }
                processAuditResult(rawMessage);
            } catch (WebmasterException e) {
                // скорей всего не смогли записать в БД, кидаем дальше, чтобы попытаться еше раз
                log.error("Failed to process processing audit result", e);
                throw e;
            } catch (Throwable t) {
                // что то пошло не так, просто логгируем, пытаться снова смысла нет
                log.error("Unexpected error processing audit result", t);
            }
        }

        if (enableLogging) {
            log.info("All messages processed");
        }
        messageContainer.commit();
    }

    private void processAuditResult(byte[] data) {
        try {
            THostCheckResult auditResult = THostCheckResult.parseFrom(data);
            if (enableLogging) {
                log.info("Got audit result for {}", auditResult.getDesktopHost());
            }

            if (!verifyAuditResult(auditResult)) {
                return;
            }

            WebmasterHostId hostId = IdUtils.urlToHostId(auditResult.getDesktopHost());
            var source = auditResult.getSource();

            boolean shouldProcess = false;
            switch (source) {
                case WEBMASTER -> {
                    if (enableLogging) {
                        log.info("Got manual webmaster audit results for {}", hostId);
                    }
                    shouldProcess = shouldProcessManualAuditResult(hostId, auditResult);
                }
                case SAMOVAR -> {
                    if (enableLogging) {
                        log.info("Got regular samovar audit results for {}", hostId);
                    }
                    shouldProcess = shouldProcessRegularAuditResult(hostId, auditResult);
                }
                default -> log.info("Skipping host {} with unknown source {}", hostId, source);
            }

            if (shouldProcess) {
                updateProblem(hostId, auditResult);

                try {
                    RetryUtils.execute(RETRY_POLICY, () -> hostMobileAuditQueueYDao.deleteHost(hostId));
                } catch (InterruptedException e) {
                    log.error("Failed to update host mobile audit data", e);
                    throw new WebmasterException("Failed to update host mobile audit data",
                            new WebmasterErrorResponse.YDBErrorResponse(getClass(), e), e);

                }
            }

            if (enableLogging) {
                log.info("Finished processing host {}", hostId);
            }
        } catch (Exception e) {
            log.error("Error processing result", e);
        }
    }

    private boolean shouldProcessManualAuditResult(WebmasterHostId hostId, THostCheckResult auditResult) {
        var req = hostMobileAuditQueueYDao.getHost(hostId);
        if (req == null) {
            log.error("Host {} not found in audit queue", hostId);
            return false;
        }

        if (isMobileFriendly(auditResult, hostId)) {
            return true;
        }

        EHostCheckStatus auditStatus = auditResult.getStatus();

        // если isMobileFriendly = false, to reason всегда проставлен
        var resolution = robotReasonToMobileAuditResolution(auditStatus);
        if (resolution != MobileAuditResolution.INTERNAL_ERROR) {
            return true;
        }

        // особый случай: ошибка в контуре проверки
        if (req.getRetriesCount() < MAX_TIMEOUT_RETRIES) {
            // в периодической таске сделаем ретрай
            log.info("Internal error while checking host {}, reason {}, retry {}", hostId, auditStatus, req.getRetriesCount());
            return false;
        } else {
            // обрабатываем результат только если сейчас есть незавершенная перепроверка
            boolean shouldProcess = isProblemRecheckInProgress(hostId);
            if (!shouldProcess) {
                // ретраи кончились и дальше обрабатывать не будем, так что удалим из очереди здесь
                try {
                    RetryUtils.execute(RETRY_POLICY, () -> hostMobileAuditQueueYDao.deleteHost(hostId));
                } catch (InterruptedException e) {
                    log.error("Failed to update host mobile audit data", e);
                    throw new WebmasterException("Failed to update host mobile audit data",
                            new WebmasterErrorResponse.YDBErrorResponse(getClass(), e), e);

                }
            }

            log.info("Internal error while checking host {}, reason {}, problem recheck in progress {}", hostId, auditStatus, shouldProcess);
            return shouldProcess;
        }
    }

    private boolean isProblemRecheckInProgress(WebmasterHostId hostId) {
        var problem = siteProblemsService.getRealTimeProblemInfo(hostId, SiteProblemTypeEnum.NOT_MOBILE_FRIENDLY);
        if (problem != null && problem.getState() == SiteProblemState.PRESENT) {
            var recheckInfo = siteProblemsRecheckYDao.getRecheckInfo(hostId, SiteProblemTypeEnum.NOT_MOBILE_FRIENDLY);
            if (recheckInfo != null) {
                return recheckInfo.getState(problem.getLastUpdate()).isInProgres;
            }
        }

        return false;
    }

    private boolean shouldProcessRegularAuditResult(WebmasterHostId hostId, THostCheckResult auditResult) {
        if (!allVerifiedHostsCacheService.contains(hostId)) {
            if (enableLogging) {
                log.info("Skipping non WM host {}", hostId);
            }

            return false;
        }

        if (!mirrorService2.isMainMirror(hostId)) {
            if (enableLogging) {
                log.info("Skipping non main mirror host {}", hostId);
            }

            return false;
        }

        if (isMobileFriendly(auditResult, hostId)) {
            return true;
        }

        var resolution = robotReasonToMobileAuditResolution(auditResult.getStatus());
        return resolution != MobileAuditResolution.INTERNAL_ERROR;
    }

    private boolean verifyAuditResult(THostCheckResult auditResult) {
        if (!auditResult.hasDesktopHost()) {
            log.error("Message doesn't have host");
            return false;
        }

        if (!auditResult.hasSource()) {
            log.error("Message doesn't have source");
            return false;
        }

        if (!auditResult.hasStatus()) {
            log.error("Message doesn't have reason");
            return false;
        }

        return true;
    }

    private String mobileHost;
    private void updateProblem(WebmasterHostId hostId, THostCheckResult auditResult) {
        mobileHost = cleanUrl(auditResult.getMobileHost());
        var source = auditResult.getSource();
        var finishDate = new DateTime(auditResult.getFinishTime() * 1000L);
        ProblemSignal problemSignal;
        List<MobileUrlSample> problemUrlSamples;
        MobileAuditResolution resolution = null;
        Set<NotMobileFriendlyStatus> notFriendlyStatuses = new HashSet<>();

        if (auditResult.getStatus() != EHostCheckStatus.IS_MOBILE_FRIENDLY) {
            notFriendlyStatuses.add(NotMobileFriendlyStatus.NOT_MOBILE_FRIENDLY);
        }

        if (abtService.isInExperiment(hostId, "MOBILE_FRIENDLY_EXTENDED")) {
            if (hasHostReplacedProblem(auditResult)) {
                notFriendlyStatuses.add(NotMobileFriendlyStatus.BAD_HOST_REPLACED);
            }

            if (hasAlternateProblem(auditResult)) {
                notFriendlyStatuses.add(NotMobileFriendlyStatus.BAD_LINK_ALTERNATE);
            }
        }

        if (notFriendlyStatuses.size() == 0 || hostMobileAuditResultsOverrideYDao.isExists(hostId)) {
            log.info("Host {} is mobile friendly", hostId);
            problemUrlSamples = new ArrayList<>();
            problemSignal = new ProblemSignal(SiteProblemTypeEnum.NOT_MOBILE_FRIENDLY, SiteProblemState.ABSENT, finishDate);
            updateSolomonCounters(source, EHostCheckStatus.IS_MOBILE_FRIENDLY.toString());
        } else {
            // если isMobileFriendly = false, to reason всегда проставлен

            var reason = auditResult.getStatus();
            if (notFriendlyStatuses.contains(NotMobileFriendlyStatus.NOT_MOBILE_FRIENDLY)) {
                updateSolomonCounters(source, reason.toString());
            }
            resolution = robotReasonToMobileAuditResolution(reason);

            //здесь inExpCheck не нужно, т.к. в сэте ничего не появится из-за предыдущего inExpCheck
            if (notFriendlyStatuses.contains(NotMobileFriendlyStatus.BAD_HOST_REPLACED)) {
                updateSolomonCounters(source, NotMobileFriendlyStatus.BAD_HOST_REPLACED.toString());
                resolution = MobileAuditResolution.NO_MOBILE_FROM_DESKTOP;
            }
            if (notFriendlyStatuses.contains(NotMobileFriendlyStatus.BAD_LINK_ALTERNATE)) {
                updateSolomonCounters(source, NotMobileFriendlyStatus.BAD_LINK_ALTERNATE.toString());
                resolution = MobileAuditResolution.NO_MOBILE_FROM_DESKTOP;
            }

            if (resolution == null) {
                log.error("Unexpected reason {} for host {}", reason, hostId);
                return;
            }

            log.info("Host {} is NOT mobile friendly, reason {}", hostId, resolution);

            if (resolution == MobileAuditResolution.INTERNAL_ERROR) {
                problemUrlSamples = new ArrayList<>();
                problemSignal = null;
            } else {
                problemUrlSamples = processAuditResultSamples(resolution, hostId, auditResult);
                
                boolean hasBadReplacedSamples = problemUrlSamples.stream().anyMatch(s -> s.getSampleType().equals(NotMobileFriendlyStatus.BAD_HOST_REPLACED));
                if (!hasBadReplacedSamples) {
                    //сигнал про реплейсы может стать нерелевантным из-за особенностей отношений с альтернейтами
                    notFriendlyStatuses.remove(NotMobileFriendlyStatus.BAD_HOST_REPLACED);
                    if (notFriendlyStatuses.size() == 0) {
                        log.warn("Shouldn't happen. Or so I've been told. Empty HostMobileAudit samples due to badHostReplaced on urls with good alternates only");
                    }
                }

                problemSignal = new ProblemSignal(new SiteProblemContent.NotMobileFriendly(resolution, NotMobileFriendlyStatus.getAlertFlag(notFriendlyStatuses)), finishDate);
            }
        }

        log.info("Total samples: {}", problemUrlSamples.size());
        MobileAuditResolution res = resolution;
        try {
            RetryUtils.execute(RETRY_POLICY, () -> {
                if (res == MobileAuditResolution.INTERNAL_ERROR) {
                    siteProblemsRecheckYDao.updateRecheckFailed(hostId, SiteProblemTypeEnum.NOT_MOBILE_FRIENDLY, true);
                } else {
                    // сохраним новые примеры или удалим предыдущие
                    List<String> serializedSamples = serializeSamples(problemUrlSamples);
                    checklistPageSamplesService.saveSamples(hostId, ChecklistSamplesType.NOT_MOBILE_FRIENDLY, serializedSamples);
                    siteProblemsService.updateRealTimeProblem(hostId, problemSignal, false, true);
                }
            });
        } catch (InterruptedException e) {
            log.error("Failed to update host mobile audit data", e);
            throw new WebmasterException("Failed to update host mobile audit data",
                    new WebmasterErrorResponse.YDBErrorResponse(getClass(), e), e);

        }
    }

    private boolean isMobileFriendly(THostCheckResult auditResult, WebmasterHostId hostId) {
        if (abtService.isInExperiment(hostId, "MOBILE_FRIENDLY_EXTENDED")) {
            return ((auditResult.getStatus() == EHostCheckStatus.IS_MOBILE_FRIENDLY) &&
                    !hasAlternateProblem(auditResult) && !hasHostReplacedProblem(auditResult));
        }

        return (auditResult.getStatus() == EHostCheckStatus.IS_MOBILE_FRIENDLY);
    }

    private void updateSolomonCounters(EHostCheckSource source, String reasonStr) {
        String sourceStr = source.toString();
        solomonMetricMap.get(Pair.of(sourceStr, reasonStr)).update();
        solomonMetricMap.get(Pair.of(SolomonGroupingUtil.AGGREGATE_LABEL_VALUE, reasonStr)).update();
        solomonMetricMap.get(Pair.of(sourceStr, SolomonGroupingUtil.AGGREGATE_LABEL_VALUE)).update();
        solomonMetricMap.get(Pair.of(SolomonGroupingUtil.AGGREGATE_LABEL_VALUE, SolomonGroupingUtil.AGGREGATE_LABEL_VALUE)).update();
    }

    private List<MobileUrlSample> processAuditResultSamples(MobileAuditResolution resolution, WebmasterHostId hostId, THostCheckResult auditResult) {
        List<MobileUrlSample> mobileUrlSamples = new ArrayList<>();
        if (auditResult.hasSample()) {
            List<TUrlCheckResult> sampleUrls = auditResult.getSample().getResultsList();
            for (var sampleUrl : sampleUrls) {
                processSampleUrl(resolution, sampleUrl, mobileUrlSamples, hostId);
            }

            if (resolution.isMordaProblem()) {
                try {
                    var mordaUrl = new URL(IdUtils.hostIdToUrl(hostId));
                    boolean mordaAdded = mobileUrlSamples.stream().anyMatch(
                            s -> mordaUrl.toExternalForm().equals(s.getUrl().toExternalForm()));
                    if (!mordaAdded) {
                        mobileUrlSamples.add(0, new MobileUrlSample(NotMobileFriendlyStatus.NOT_MOBILE_FRIENDLY, mordaUrl, null, null, null, null));
                    }
                } catch (MalformedURLException e) {
                    // should not happen
                }
            }
        }

        return mobileUrlSamples;
    }

    private void processSampleUrl(MobileAuditResolution resolution, TUrlCheckResult sampleUrl,
                                  List<MobileUrlSample> outSamples, WebmasterHostId hostId) {
            
        if (sampleUrl.getStatus() == EUrlCheckStatus.MOBILE_FRIENDLY && resolution != MobileAuditResolution.FEW_UNIQUE_URLS
                && resolution != MobileAuditResolution.NO_MOBILE_FROM_DESKTOP) {

                // урл мобилопригоден, в семплах не нужен
                log.debug("Skipping sample url {}", sampleUrl.getOriginalUrl());
                return;
        }

        URL originalUrl, targetUrl = null, alternateUrl = null;

        String originalUrlS = cleanUrl(sampleUrl.getOriginalUrl());
        try {
            originalUrl = new URL(originalUrlS);

            if (sampleUrl.hasTargetUrl()) {
                String targetUrlS = cleanUrl(sampleUrl.getTargetUrl());
                if (!StringUtils.isEmpty(targetUrlS)) {
                    targetUrl = new URL(targetUrlS);
                }
            } else {
                targetUrl = new URL(originalUrlS);
            }

            MobileUrlCheckStatus status = robotUrlCheckStatusToMobileUrlCheckStatus(sampleUrl.getStatus());
            if (status != null) { //hasMobileFriendlyProblem
                outSamples.add(new MobileUrlSample(NotMobileFriendlyStatus.NOT_MOBILE_FRIENDLY, originalUrl, targetUrl, null, null, status));
            }

            if (sampleUrl.hasAlternateUrl()) {
                String alternateUrlS = cleanUrl(sampleUrl.getAlternateUrl());
                if (!StringUtils.isEmpty(alternateUrlS)) {
                    alternateUrl = new URL(alternateUrlS);
                }
            }

            URL expectedUrl = getExpectedUrl(originalUrl, targetUrl);

            if (abtService.isInExperiment(hostId, "MOBILE_FRIENDLY_EXTENDED")) {
                if (hasAlternateProblem(sampleUrl)) {
                    outSamples.add(new MobileUrlSample(NotMobileFriendlyStatus.BAD_LINK_ALTERNATE, originalUrl, targetUrl, alternateUrl, expectedUrl, null));
                }

                if (hasHostReplacedProblem(sampleUrl) && !sampleUrl.hasAlternateUsed()) {
                    //есть проблема с реплейсом и альтернейта нет вообще. В остальных случаях урл в семплах не нужен
                    outSamples.add(new MobileUrlSample(NotMobileFriendlyStatus.BAD_HOST_REPLACED, originalUrl, targetUrl, alternateUrl, expectedUrl, null));
                }
            }
        } catch (MalformedURLException e) {
            log.error("Skipping malformed URL: {}", originalUrlS);
        }

    }

    private URL getExpectedUrl(URL originalUrl, URL targetUrl) {
        //мобильный url, который робот может сопоставить с десктопным
        try {
            if (Strings.isNullOrEmpty(mobileHost)) {
                return originalUrl;
            }
            
            return new URL(mobileHost + originalUrl.getFile());
        } catch (MalformedURLException e) {
            return null;
        }
    }

    private String cleanUrl(String url) {
        Matcher urlMatcher = KEY_PATTERN.matcher(url);
        if (urlMatcher.find()) {
            url = urlMatcher.group(1);
        }

        return url;
    }

    private List<String> serializeSamples(List<MobileUrlSample> samples) {
        return samples.stream().map(sample -> {
            try {
                return JsonDBMapping.OM.writeValueAsString(sample);
            } catch (JsonProcessingException e) {
                throw new WebmasterException("Json error",
                        new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), "Unknown json error"));
            }
        }).collect(Collectors.toList());
    }

    private static MobileAuditResolution robotReasonToMobileAuditResolution(EHostCheckStatus reason) {
        return switch (reason) {
            case HOST_IS_BANNED -> MobileAuditResolution.NO_DOWNLOADED_URLS;
            case MORDA_NOT_VALID -> MobileAuditResolution.FAILED_TO_DOWNLOAD_BASE_URL;
            case MORDA_BAD_TARGET_OWNER -> MobileAuditResolution.BAD_TARGET_OWNER_FOR_BASE_URL;
            case MORDA_MOBILE_UNFRIENDLY -> MobileAuditResolution.BASE_URL_NOT_MOBILE_FRIENDLY;
            case FEW_MOBILE_FRIENDLY_URLS -> MobileAuditResolution.FEW_MOBILE_FRIENDLY_URLS;
            case FEW_UNIQUE_TARGET_URLS -> MobileAuditResolution.FEW_UNIQUE_URLS;
            case FEW_VALID_URLS -> MobileAuditResolution.FEW_DOWNLOADED_URLS;
            case HOST_IS_DEAD, FEW_CHECKED_URLS, FEW_ROTORED_URLS, MORDA_NOT_CHECKED, MORDA_NOT_ROTORED -> MobileAuditResolution.INTERNAL_ERROR;
            default -> null;
        };
    }

    private static MobileUrlCheckStatus robotUrlCheckStatusToMobileUrlCheckStatus(EUrlCheckStatus status) {
        return switch (status) {
            case NOT_VIEW_PORT -> MobileUrlCheckStatus.NOT_VIEW_PORT;
            case HAS_SILVERLIGHT -> MobileUrlCheckStatus.HAS_SILVERLIGHT;
            case HAS_APPLET -> MobileUrlCheckStatus.HAS_APPLET;
            case HAS_FLASH -> MobileUrlCheckStatus.HAS_FLASH;
            case TOO_SMALL_TEXT -> MobileUrlCheckStatus.TOO_SMALL_TEXT;
            case HORIZONTAL_OVERSIZE -> MobileUrlCheckStatus.HORIZONTAL_OVERSIZE;
            default -> null;
        };
    }

    private SolomonCounter createSolomonCounter(String source, String reason) {
        var key = SolomonKey.create(SolomonSensor.LABEL_CATEGORY, "host_mobile_audit")
                .withLabel("audit_result_samovar", reason)
                .withLabel("source", source)
                .withLabel(SolomonSensor.LABEL_INDICATOR, Indicators.COUNT);

        return solomonMetricRegistry.createCounter(solomonMetricConfiguration, key);
    }

    private List<String> getReasons() {
        List<String> res = Arrays.stream(EHostCheckStatus.values()).map(Enum::toString).collect(Collectors.toList());

        res.add(NotMobileFriendlyStatus.BAD_HOST_REPLACED.toString());
        res.add(NotMobileFriendlyStatus.BAD_LINK_ALTERNATE.toString());
        return res;
    }

    private boolean hasAlternateProblem(THostCheckResult checkResult) {
        if (!checkResult.hasAlternateUsed()) {
            return false; //none. No alternate urls - ok for mobile friendly
        }
        return !checkResult.getAlternateUsed();
    }

    private boolean hasAlternateProblem(TUrlCheckResult checkResult) {
        if (!checkResult.hasAlternateUsed()) {
            return false; //none. No alternate url - ok for mobile friendly
        }
        return !checkResult.getAlternateUsed();
    }

    private boolean hasHostReplacedProblem(THostCheckResult checkResult) {
        if (!checkResult.hasHostReplaced()) {
            return false; //none. No replace urls - ok for mobile friendly
        }
        return !checkResult.getHostReplaced();
    }

    private boolean hasHostReplacedProblem(TUrlCheckResult checkResult) {
        if (!checkResult.hasHostReplaced()) {
            return false; //none. No replace url - ok for mobile friendly
        }
        return !checkResult.getHostReplaced();
    }  
}
