package ru.yandex.webmaster3.api.turbo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;

import ru.yandex.autodoc.common.doc.annotation.Description;
import ru.yandex.webmaster3.core.util.enums.EnumResolver;
import ru.yandex.webmaster3.core.turbo.model.error.TurboRawError;

/**
 * Created by ifilippov5 on 28.05.18.
 */
public enum TurboApiErrorType {

    BANNED_DOCUMENT(true, false, "description", "turbo-banned-document", "Banned.Document"),
    BANNED_FEED(true, false, null, "turbo-banned-feed", "Banned.Feed"),
    DOCUMENT_QUOTA_EXCEEDED_IMAGES(true, true, "url", "turbo-document-quota-exceeded-image", "Document.QuotaExceeded.Images"),
    DUPLICATE_DOCUMENT(false, false, "url", "turbo-duplicate-document", "Duplicate.Document"),
    DUPLICATE_FEED(false, false, null, "turbo-duplicate-feed", "Duplicate.Feed"),
    EMAIL_BAD_DOMAIN(true, false, "email", "turbo-email-bad-domain", "Email.BadDomain"),
    EMAIL_BAD_DOMAIN_INVALID(true, false, "email", "turbo-email-bad-domain-invalid", "Email.BadDomain.Invalid"),
    EMAIL_BAD_DOMAIN_NOT_ALLOWED(true, false, "email", "turbo-email-bad-domain-not-allowed", "Email.BadDomain.NotAllowed"),
    EMAIL_INVALID_FORMAT(false, false, "email", "turbo-email-invalid-format", "Email.InvalidFormat"),
    //IMAGES_ITEM_IMAGES_MISSING(true, false, "url", "turbo-images-item-images-missing", "Images.ItemImagesMissing"),
    FETCH_QUOTA_EXCEEDED_FEEDS(true, false, null, "turbo-fetch-quota-exceeded-feeds", "Fetch.QuotaExceeded.Feeds"),
    FETCH_QUOTA_EXCEEDED_IMAGES(false, false, null, "turbo-fetch-quota-exceeded-images", "Fetch.QuotaExceeded.Images"),
    FETCH_QUOTA_EXCEEDED_ITEMS(false, false, null, "turbo-fetch-quota-exceeded-items", "Fetch.QuotaExceeded.Items"),
    //@Description("Фид запрещен настройками robots.txt")
    //FETCHER_DISALLOWED_IN_ROBOTS(TurboError.IS_DISALLOWED_IN_ROBOTS, true, false, null, "", "Fetcher.Http"),
    @Description("Сервер ответил ошибкой 4xx-5xx")
    FETCHER_HTTP(true, false, "http_code", "turbo-parser-http", "Parser.Http3xx", "Parser.Http4xx", "Parser.Http5xx"),
    //@Description("Сервер не ответил вообще (и другие ошибки с внутренними статусами 1000+)")
    //FETCHER_NO_RESPONSE(TurboError.IS_DISALLOWED_IN_ROBOTS.negate(), true, false, null, "Fetcher.Http"),
    IMAGES_FETCH_ERROR(true, true, "url", "turbo-images-fetch-error", "Images.FetchError", "Images.Unavailable", "Skip.Images"),
    IMAGES_STILL_WAITING(true, true, "url", "turbo-images-still-waiting", "Images.StillWaiting"),
    INTERNAL(true, false, null, "turbo-internal", "Internal", "Storage.Internal", "Fetcher.Internal", "Parser.Internal",
                     "Images.FetchInternalError", "Fetch.Internal", "Images.Internal", "Internal.Error"),
    PARSER_AD_BAD_ID(true, false, "type", "turbo-parser-ad-bad-id", "Parser.Ad.BadId"),
    PARSER_AD_BAD_TYPE(false, true, null, "turbo-parser-ad-bad-type", "Parser.Ad.BadType"),
    PARSER_AD_DUPLICATE(false, false, "type", "turbo-parser-ad-duplicate", "Parser.Ad.Duplicate"),
    PARSER_AD_MISSING_TURBO_ID(false, false, null, "turbo-parser-ad-missing-turbo-id", "Parser.Ad.MissingTurboId"),
    PARSER_AD_UNKNOWN_REF(true, false, "url", "turbo-parser-ad-unknown-ref", "Parser.Ad.UnknownRef"),
    PARSER_AD_UNKNOWN_TYPE(true, false, "type", "turbo-parser-ad-unknown-type", "Parser.Ad.UnknownType"),
    PARSER_BAD_FORMAT(true, true, null, "turbo-parser-bad-format", "Parser.BadFormat"),
    PARSER_BAD_FORMAT_BROKEN_UTF(false, true, null, "turbo-parser-bad-format-broken-utf", "Parser.BadFormat.BrokenUtf"),
    PARSER_BAD_FORMAT_DUPLICATE_TAG(false, true, "tag", "turbo-parser-bad-format-duplicate-tag", "Parser.BadFormat.DuplicateTag"),
    PARSER_BAD_FORMAT_MISSING_TAG(true, true, "tag", "turbo-parser-bad-format-missing-tag", "Parser.BadFormat.MissingTag"),
    PARSER_BAD_FORMAT_NO_TEXT(false, true, "tag", "turbo-parser-bad-format-no-text", "Parser.BadFormat.NoText"),
    PARSER_BAD_FORMAT_TEXT_TOO_LONG(false, true, "context", "turbo-parser-bad-format-text-too-long", "Parser.BadFormat.TextTooLong"),
    PARSER_BAD_FORMAT_UNKNOWN_PARENT_CATEGORY(false, true, null, "turbo-parser-bad-format-unknown-parent-category", "Parser.BadFormat.UnknownParentCategory"),
    PARSER_BAD_FORMAT_UNEXPECTED_TAG(false, true, "tag", "turbo-parser-bad-format-unexpected-tag", "Parser.BadFormat.UnexpectedTag"),
    PARSER_BAD_LOGO(true, false, null, "turbo-parser-bad-logo", "Parser.BadLogo"),
    PARSER_DUPLICATE_URLS(true, true, "url", "turbo-parser-duplicate-urls", "Parser.DuplicateUrls"),
    PARSER_EMPTY_XML(true, false, null, "turbo-parser-empty-xml", "Parser.EmptyXML"),
    PARSER_GOAL_BAD_ID(false, true, null, "turbo-parser-goal-bad-id", "Parser.Goal.BadId"),
    PARSER_GOAL_DUPLICATE(false, true, null, "turbo-goal-duplicate", "Parser.Goal.Duplicate"),
    PARSER_GOAL_NO_ATTR(false, true, null, "turbo-parser-goal-no-attr", "Parser.Goal.NoAttr"),
    PARSER_GOAL_UNKNOWN_TYPE(true, true, null, "turbo-parser-goal-unknown-type", "Parser.Goal.UnknownType"),
    PARSER_ITEM_BAD_ENCLOSURE(false, true, null, "turbo-parser-item-bad-enclosure", "Parser.Item.BadEnclosure"),
    PARSER_ITEM_BAD_PDA_URL(false, true, "url", "turbo-parser-item-bad-pda-url", "Parser.Item.BadPdaUrl"),
    PARSER_ITEM_BAD_PUB_DATE(true, true, "date", "turbo-parser-item-bad-pub-date", "Parser.Item.BadPubDate"),
    PARSER_ITEM_BAD_RELATED(false, true, null, "turbo-parser-item-bad-related", "Parser.Item.BadRelated"),
    PARSER_ITEM_BAD_SOURCE(false, true, "url", "turbo-parser-item-bad-source", "Parser.Item.BadSource"),
    PARSER_ITEM_BAD_TITLE(true, true, "url", "turbo-parser-item-bad-title", "Parser.Item.BadTitle"),
    PARSER_ITEM_BAD_URL(true, true, "url", "turbo-parser-item-bad-url", "Parser.Item.BadUrl"),
    PARSER_ITEM_EXTERNAL_LINK(true, true, "url", "turbo-parser-item-external-link", "Parser.Item.ExternalLink", "Document.BadDomain", "Document.BadDomain.NotAllowed", "Document.Related.BadDomain.NotAllowed"),
    //PARSER_ITEM_EXTERNAL_RELATED_LINK(false, true, "url", "turbo-parser-item-external-related", "Parser.Item.ExternalRelatedLink"),
    PARSER_ITEM_HTML_TAGS_IN_TEXT(false, true, "url", "turbo-parser-item-html-tags-in-text", "Parser.Item.HtmlTagsInText"),
    PARSER_ITEM_INPAGE_ALIAS_NOT_SPECIFIED(false, true, null, "turbo-parser-item-inpage-alias-not-specified", "Parser.Item.InpageAliasNotSpecified"),
    PARSER_ITEM_INVALID_COVER_MARKUP(true, true, null, "turbo-parser-item-invalid-cover-markup", "Parser.Item.InvalidCoverMarkup"),
    PARSER_ITEM_INVALID_TURBO_MARKUP(true, true, null, "turbo-parser-item-invalid-turbo-markup", "Parser.Item.InvalidTurboMarkup"),
    PARSER_ITEM_MORE_THAN_ONE_INPAGE_AD_BLOCK(false, true, null, "turbo-parser-item-more-than-one-inpage-ad-block", "Parser.Item.MoreThanOneInpageAdBlock"),
    PARSER_ITEM_NO_PUB_DATE(true, true, "url", "turbo-parser-item-no-pub-date", "Parser.Item.NoPubDate"),
    PARSER_ITEM_NO_TEXT(true, true, null, "turbo-parser-item-no-text", "Parser.Item.NoText"),
    PARSER_ITEM_NO_URL(true, true, null, "turbo-parser-item-no-url", "Parser.Item.NoUrl"),
    PARSER_ITEM_NON_TURBO(true, true, null, "turbo-parser-item-non-turbo", "Parser.Item.NonTurbo"),
    PARSER_ITEM_SAME_INPAGE_ALIAS_AND_MAIN_ALIAS(false, true, null, "turbo-parser-item-same-inpage-alias-and-main-alias", "Parser.Item.SameInpageAliasAndMainAlias"),
    PARSER_ITEM_TOO_MANY_RELATED_ITEMS(false, true, "url", "turbo-parser-item-too-many-related-items", "Parser.Item.TooManyRelatedItems"),
    PARSER_ITEM_TURBO_CONTENT_BAD_SOCIAL(false, true, "info", "turbo-parser-item-turbo-content-bad-social", "Parser.Item.TurboContent.BadSocial"),
    PARSER_ITEM_TURBO_CONTENT_CONTENT_REQUIRED(false, true, null, "turbo-parser-item-turbo-content-content-required", "Parser.Item.TurboContent.ContentRequired"),
    PARSER_ITEM_TURBO_CONTENT_CONTRADICTING_ATTRIBUTES(false, true, null, "turbo-parser-item-turbo-content-contradicting-attributes", "Parser.Item.TurboContent.ContradictingAttributes"),
    PARSER_ITEM_TURBO_CONTENT_CUSTOM_HTML_INVALID_CLASS_ATTR(false, true, "turbo-parser-item-turbo-content-custom-html-invalid-class-attr", "Parser.Item.TurboContent.CustomHtmlInvalidClassAttr"),
    PARSER_ITEM_TURBO_CONTENT_CUSTOM_HTML_INVALID_ID_ATTR(false, true, null, "turbo-parser-item-turbo-content-custom-html-invalid-id-attr", "Parser.Item.TurboContent.CustomHtmlInvalidIdAttr"),
    PARSER_ITEM_TURBO_CONTENT_DUPLICATE_ELEMENT_ID(false, true, null, "turbo-parser-item-turbo-content-duplicate-element-id", "Parser.Item.TurboContent.DuplicateElementId"),
    PARSER_ITEM_TURBO_CONTENT_DUPLICATE_ON_PAGE_WIDGET_FEEDBACK(false, true, null, "turbo-parser-item-turbo-content-duplicate-on-page-widget-feedback", "Parser.Item.TurboContent.DuplicateOnPageWidgetFeedback"),
    PARSER_ITEM_TURBO_CONTENT_EMPTY_BLOCK_IN_DYNAMIC_FORM(false, true, null, "turbo-parser-item-turbo-content-empty-block-in-dynamic-form", "Parser.Item.TurboContent.EmptyBlockInDynamicForm"),
    PARSER_ITEM_TURBO_CONTENT_FOLD_BLOCK_WITH_UNSUPPORTED_TAG(false, true, null, "turbo-parser-item-turbo-content-fold-block-with-unsupported-tag", "Parser.Item.TurboContent.FoldBlockWithUnsupportedTag"),
    PARSER_ITEM_TURBO_CONTENT_HTML_TAGS_IN_TEXT(false, true, "url", "turbo-parser-item-turbo-content-html-tags-in-text", "Parser.Item.TurboContent.HtmlTagsInText"),
    PARSER_ITEM_TURBO_CONTENT_INVALID_BLOCK_TYPE_IN_DYNAMIC_FORM(false, true, null, "turbo-parser-item-turbo-content-invalid-block-type-in-dynamic-form", "Parser.Item.TurboContent.InvalidBlockTypeInDynamicForm"),
    PARSER_ITEM_TURBO_CONTENT_INVALID_HTML_CLOSING_TAG(false, true, null, "turbo-parser-item-turbo-content-invalid-html-closing-tag", "Parser.Item.TurboContent.InvalidHtmlClosingTag"),
    PARSER_ITEM_TURBO_CONTENT_INVALID_LINE_TYPE_IN_DYNAMIC_FORM(false, true, null, "turbo-parser-item-turbo-content-invalid-line-type-in-dynamic-form", "Parser.Item.TurboContent.InvalidLineTypeInDynamicForm"),
    PARSER_ITEM_TURBO_CONTENT_INVALID_VALUE(false, true, "info", "turbo-parser-item-turbo-content-invalid-value", "Parser.Item.TurboContent.InvalidValue"),
    PARSER_ITEM_TURBO_CONTENT_INVALID_URL(false, true, "info", "turbo-parser-item-turbo-content-invalid-url", "Parser.Item.TurboContent.InvalidUrl"),
    PARSER_ITEM_TURBO_CONTENT_INVALID_URL_FRAGMENT(false, true, null, "turbo-parser-item-turbo-content-invalid-url-fragment", "Parser.Item.TurboContent.InvalidUrlFragment"),
    PARSER_ITEM_TURBO_CONTENT_MULTIPLE_BLOCKS_OF_SAME_TYPE_IN_DYNAMIC_FORM(false, true, null, "turbo-parser-item-turbo-content-multiple-blocks-of-same-type-in-dynamic-form", "Parser.Item.TurboContent.MultipleBlocksOfTheSameTypeInDynamicForm"),
    PARSER_ITEM_TURBO_CONTENT_MULTIPLE_CALL_BUTTON_IN_WIDGET_FEEDBACK(false, true, "url", "turbo-parser-item-turbo-content-multiple-call-button-widget-feedback", "Parser.Item.TurboContent.MultipleCallButtonInWidgetFeedback"),
    PARSER_ITEM_TURBO_CONTENT_NO_CARDS(false, true, "url", "turbo-parser-item-turbo-content-no-cards", "Parser.Item.TurboContent.NoCards"),
    PARSER_ITEM_TURBO_CONTENT_NO_ITEMS(false, true, null, "turbo-parser-item-turbo-content-no-items", "Parser.Item.TurboContent.NoItems"),
    PARSER_ITEM_TURBO_CONTENT_NO_ITEMS_IN_CAROUSEL(false, true, "url", "turbo-parser-item-turbo-content-no-item-in-carousel", "Parser.Item.TurboContent.NoItemsInCarousel"),
    PARSER_ITEM_TURBO_CONTENT_NO_ITEMS_IN_COLLECTION(false, true, "url", "turbo-parser-item-turbo-content-no-items-in-collection", "Parser.Item.TurboContent.NoItemsInCollection"),
    PARSER_ITEM_TURBO_CONTENT_NO_ITEMS_IN_HISTOGRAM(false, true, "url", "turbo-parser-item-turbo-content-no-items-in-histogram", "Parser.Item.TurboContent.NoItemsInHistogram"),
    PARSER_ITEM_TURBO_CONTENT_NO_ITEMS_IN_TABS(false, true, "url", "turbo-parser-item-turbo-content-no-items-in-tabs", "Parser.Item.TurboContent.NoItemsInTabs"),
    PARSER_ITEM_TURBO_CONTENT_NO_ITEMS_IN_WIDGET_FEEDBACK(false, true, "url", "turbo-parser-item-turbo-content-no-items-in-widget-feedback", "Parser.Item.TurboContent.NoItemsInWidgetFeedback"),
    PARSER_ITEM_TURBO_CONTENT_NO_REQUIRED_ATTR(false, true, "info", "turbo-parser-item-turbo-content-no-required-attr", "Parser.Item.TurboContent.NoRequiredAttr"),
    PARSER_ITEM_TURBO_CONTENT_NO_TEXT_AND_ICON(false, true, "url", "turbo-parser-item-turbo-content-no-text-and-icon", "Parser.Item.TurboContent.NoTextAndIcon"),
    PARSER_ITEM_TURBO_CONTENT_NO_VALID_SOCIAL(false, true, "info", "turbo-parser-item-turbo-content-no-valid-social", "Parser.Item.TurboContent.NoValidSocial"),
    PARSER_ITEM_TURBO_CONTENT_NOT_ALL_GROUP_ATTRIBUTES_SPECIFIED(false, true, null, "turbo-parser-item-not-all-group-attributes-specified", "Parser.Item.NotAllGroupAttributesSpecified", "Parser.Item.TurboContent.NotAllGroupAttributesSpecified"),
    PARSER_ITEM_TURBO_CONTENT_RECURSION_DEPTH_LIMIT_REACHED(true, false, null, "turbo-parser-item-turbo-content-recursion-depth-limit-reached", "Parser.Item.TurboContent.RecursionDepthLimitReached"),
    PARSER_ITEM_TURBO_CONTENT_SELF_CONSTRUCTED_TURBO_URL(false, true, null, "turbo-parser-item-turbo-content-self-constructed-turbo-url", "Parser.Item.TurboContent.SelfConstructedTurboUrl"),
    PARSER_ITEM_TURBO_CONTENT_TOO_FEW_OPTIONS_IN_DYNAMIC_FORM(false, true, null, "turbo-parser-item-turbo-content-too-few-options-in-dynamic-form", "Parser.Item.TurboContent.TooFewOptionsInDynamicForm"),
    PARSER_ITEM_TURBO_CONTENT_UNKNOWN_CARD_TYPE(false, true, "url", "turbo-parser-item-turbo-content-unknown-card-type", "Parser.Item.TurboContent.UnknownCardType"),
    PARSER_ITEM_TURBO_CONTENT_UNKNOWN_CAROUSEL_ITEM_TYPE(false, true, "info", "turbo-parser-item-turbo-content-unknown-carousel-item-type", "Parser.Item.TurboContent.UnknownCarouselItemType"),
    PARSER_ITEM_TURBO_CONTENT_UNKNOWN_ICON(false, true, "info", "turbo-parser-item-turbo-content-unknown-icon", "Parser.Item.TurboContent.UnknownIcon"),
    PARSER_ITEM_TURBO_CONTENT_UNKNOWN_WIDGET_FEEDBACK_TYPE(false, true, "info", "turbo-parser-item-turbo-content-unknown-widget-feedback", "Parser.Item.TurboContent.UnknownWidgetFeedbackType"),
    PARSER_ITEM_TURBO_CONTENT_UNSUPPORTED_TAG(true, true, null, "turbo-parser-item-turbo-content-unsupported-tag", "Parser.Item.TurboContent.UnsupportedTag"),
    PARSER_ITEM_TURBO_CONTENT_WALL_OF_TEXT(false, true, "url", "turbo-parser-item-turbo-content-wall-of-text", "Parser.Item.TurboContent.WallOfText"),
    PARSER_NO_CHANNEL(true, false, null, "turbo-parser-no-channel", "Parser.NoChannel"),
    PARSER_NO_ITEMS(true, false, null, "turbo-parser-no-items", "Parser.NoItems"),
    PARSER_NOT_RSS(true, false, null, "turbo-parser-not-rss", "Parser.NotRss"),
    PARSER_NOT_YML(true, false, null, "turbo-parser-not-yml", "Parser.NotYml"),
    //PARSER_TRACKER_BAD_ID(true, false, "type", "turbo-parser-tracker-bad-id", "Parser.Tracker.BadId"),
    PARSER_TRACKER_DUPLICATE(false, false, "type", "turbo-parser-tracker-duplicate", "Parser.Tracker.Duplicate"),
    PARSER_TRACKER_INVALID_JSON(true, false, null, "Parser.Tracker.InvalidJson"),
    PARSER_TRACKER_UNKNOWN_TYPE(true, false, "type", "turbo-parser-tracker-unknown-type", "Parser.Tracker.UnknownType"),
    PARSER_XML_ERROR(true, true, "message", "turbo-parser-xml-error", "Parser.XmlError"),
    @Description("Фид слишком большой")
    PARSER_XML_TOO_BIG(true, false, null, "turbo-parser-xml-too-big", "Parser.XMLTooBig"),
    UNKNOWN(true, false, null, "turbo-unknown", "Unknown"),
    ;

    public static final Set<String> IGNORED_ERRORS = Sets.newHashSet(
            "Parser.NoChannel",
            "Fetch.Skip",
            "Images.Skip",
            "Parser.Tracker.BadId",
            "Parser.Item.InvalidCoverMarkup",
            "Parser.Item.ExternalRelatedLink",
            "Images.ItemImagesMissing"
    );

    private final static List<TurboApiErrorType> UNKNOWN_LIST = Collections.singletonList(UNKNOWN);

    private final boolean fatal;
    private final boolean hasItemUrl;
    private final Predicate<ObjectNode> condition;
    private final String mainParam;
    private final String helpAnchor;
    private final String[] codes;

    private static final Map<String, List<TurboApiErrorType>> errorByCodes = new HashMap<>();
    static {
        for (TurboApiErrorType type : values()) {
            for (String code : type.codes) {
                errorByCodes.computeIfAbsent(code, k -> new ArrayList<>()).add(type);
            }
        }
    }

    TurboApiErrorType(boolean fatal, boolean hasItemUrl, String mainParam, String helpAnchor, String... codes) {
        this(null, fatal, hasItemUrl, mainParam, helpAnchor, codes);
    }

    TurboApiErrorType(Predicate<ObjectNode> condition, boolean fatal, boolean hasItemUrl, String mainParam, String helpAnchor, String... codes) {
        this.mainParam = mainParam;
        this.condition = condition;
        this.codes = codes;
        this.fatal = fatal;
        this.hasItemUrl = hasItemUrl;
        this.helpAnchor = helpAnchor;
    }

    public boolean isFatal() {
        return fatal;
    }

    public boolean hasItemUrl() {
        return hasItemUrl;
    }

    public String getMainParam() {
        return mainParam;
    }

    public String getHelpAnchor() {
        return helpAnchor;
    }

    @JsonIgnore
    public String[] getCodes() {
        return codes;
    }

    public static TurboApiErrorType fromRawError(TurboRawError rawError) {
        /*if (IGNORED_ERRORS.contains(rawError.getCode())) {
            return null;
        }*/
        List<TurboApiErrorType> types = errorByCodes.getOrDefault(rawError.getCode(), UNKNOWN_LIST);
        if (types.size() == 1) {
            return types.get(0);
        }
        return types.stream().filter(type -> type.condition == null || type.condition.test(rawError.getParams()))
                .findFirst().orElse(UNKNOWN);
    }

    public static TurboApiErrorType fromCode(String code) {
        if (Strings.isNullOrEmpty(code)) {
            return null;
        }
        return errorByCodes.getOrDefault(code, UNKNOWN_LIST).get(0);
    }

    public static EnumResolver<TurboApiErrorType> R = new EnumResolver<>(TurboApiErrorType.class);
}
