package ru.yandex.webmaster3.storage.user.message.content;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.joda.time.DateTime;

import ru.yandex.webmaster3.core.checklist.data.SiteProblemContent;
import ru.yandex.webmaster3.core.checklist.data.SiteProblemTypeEnum;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.feeds.feed.FeedsErrorSeverity;
import ru.yandex.webmaster3.core.http.HierarchyTypeField;
import ru.yandex.webmaster3.core.turbo.model.TurboPlatform;
import ru.yandex.webmaster3.core.turbo.model.advertising.AdvertisingPlacement;
import ru.yandex.webmaster3.core.util.IdUtils;
import ru.yandex.webmaster3.core.util.json.polymorphic.Polymorphic;
import ru.yandex.webmaster3.storage.notifications.NotificationPlaceholder;
import ru.yandex.webmaster3.storage.user.message.MessageTypeEnum;
import ru.yandex.webmaster3.storage.user.message.iks.IksMessageContent;
import ru.yandex.webmaster3.storage.user.message.iks.IksMessageType;

/**
 * WARNING
 * MessageContent'ы сериализуются/десериализуются в базу автоматом через jackson.
 * И jackson'ом же они выводятся через API. В какой-то момент может встать задача поменять формат в одном месте,
 * не задевая его в другом
 *
 * @author avhaliullin
 */
public abstract class MessageContent implements Polymorphic<MessageTypeEnum> {
    private static final ObjectMapper OM = new ObjectMapper();
    private final MessageTypeEnum type;


    protected MessageContent(MessageTypeEnum type) {
        this.type = type;
    }

    public boolean isRequiresHost() {
        return type.isRequiresHost();
    }

    public String hashString() {
        try {
            return OM.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @HierarchyTypeField
    public MessageTypeEnum getType() {
        return type;
    }

    public static abstract class HostMessageContent extends MessageContent {
        private final WebmasterHostId hostId;

        protected HostMessageContent(MessageTypeEnum type, WebmasterHostId hostId) {
            super(type);
            this.hostId = hostId;
        }

        public WebmasterHostId getHostId() {
            return hostId;
        }
    }

    public static class PlainMessage extends MessageContent {
        private final String title;
        private final String body;

        @JsonCreator
        public PlainMessage(@JsonProperty("title") String title, @JsonProperty("body") String body) {
            super(MessageTypeEnum.PLAIN);
            this.title = title;
            this.body = body;
        }

        public String getTitle() {
            return title;
        }

        public String getBody() {
            return body;
        }
    }

    public static class TrendMessage extends MessageContent {
        private final TrendType trendType;
        private final VariableType variableType;

        @JsonCreator
        public TrendMessage(@JsonProperty("trendType") TrendType trendType,
                            @JsonProperty("variableType") VariableType variableType) {
            super(MessageTypeEnum.TREND);
            this.trendType = trendType;
            this.variableType = variableType;
        }

        public TrendType getTrendType() {
            return trendType;
        }

        public VariableType getVariableType() {
            return variableType;
        }

        public enum TrendType {
            GROWTH, FALL
        }

        public enum VariableType {
            PAGES_IN_INDEX, EXCLUDED_PAGES
        }
    }

    public static class VirusMessage extends MessageContent {
        private final ProblemTypeEnum problemType;

        @JsonCreator
        public VirusMessage(@JsonProperty("problemType") ProblemTypeEnum problemType) {
            super(MessageTypeEnum.VIRUS);
            this.problemType = problemType;
        }

        public ProblemTypeEnum getProblemType() {
            return problemType;
        }

        public enum ProblemTypeEnum {
            VIRUS,
            CLEAR_VIRUS,
            WRAPPER,
            CLEAR_WRAPPER,
            XSS,
            CLEAR_XSS,
            SPAM,
            CLEAR_SPAM,
            CLOAKING,
            CLEAR_CLOAKING,
            DOORWAY,
            CLEAR_DOORWAY
        }
    }

    public static class SerpLinksUpdate extends MessageContent {
        private final String section;
        private final boolean positionsChanged;
        private final List<String> newPages;
        private final List<String> removedPages;
        private final List<PageNamesChange> newNames;

        @JsonCreator
        public SerpLinksUpdate(@JsonProperty("section") String section,
                               @JsonProperty("positionsChanged") boolean positionsChanged,
                               @JsonProperty("newPages") List<String> newPages,
                               @JsonProperty("removedPages") List<String> removedPages,
                               @JsonProperty("newNames") List<PageNamesChange> newNames) {
            super(MessageTypeEnum.SERPLINKS_UPDATE);
            this.section = section;
            this.positionsChanged = positionsChanged;
            this.newPages = newPages;
            this.removedPages = removedPages;
            this.newNames = newNames;
        }

        public String getSection() {
            return section;
        }

        public boolean isPositionsChanged() {
            return positionsChanged;
        }

        public List<String> getNewPages() {
            return newPages;
        }

        public List<String> getRemovedPages() {
            return removedPages;
        }

        public List<PageNamesChange> getNewNames() {
            return newNames;
        }

        public static class PageNamesChange {
            private final String page;
            private final List<String> newNames;

            public PageNamesChange(@JsonProperty("page") String page, @JsonProperty("newNames") List<String> newNames) {
                this.page = page;
                this.newNames = newNames;
            }

            public String getPage() {
                return page;
            }

            public List<String> getNewNames() {
                return newNames;
            }
        }
    }

    public static class Distribution extends MessageContent {
        private final UUID notificationId;
        private final int revisionId;
        private final Map<NotificationPlaceholder, String> placeholders;

        public Distribution(
                @JsonProperty("notificationId") UUID notificationId,
                @JsonProperty("revisionId") int revisionId,
                @JsonProperty("placeholders") Map<NotificationPlaceholder, String> placeholders
        ) {
            super(MessageTypeEnum.DISTRIBUTION);
            this.notificationId = notificationId;
            this.revisionId = revisionId;
            this.placeholders = placeholders;
        }

        public UUID getNotificationId() {
            return notificationId;
        }

        public int getRevisionId() {
            return revisionId;
        }

        public Map<NotificationPlaceholder, String> getPlaceholders() {
            return placeholders;
        }
    }

    @Getter
    public static class ChecklistChanges extends MessageContent {
        private final WebmasterHostId hostId;
        private final SiteProblemTypeEnum problemType;
        private final DateTime detectionDate;
        private final DateTime actualSince;
        private final SiteProblemContent problemContent;
        private final int attempt; // номер попытки для повторных писем

        public ChecklistChanges(
                @JsonProperty("hostId") WebmasterHostId hostId,
                @JsonProperty("problemType") SiteProblemTypeEnum problemType,
                @JsonProperty("detectionDate") DateTime detectionDate,
                @JsonProperty("actualSince") DateTime actualSince,
                @JsonProperty("problemContent") SiteProblemContent problemContent,
                @JsonProperty("attempt") int attempt) {
            super(MessageTypeEnum.CHECKLIST_CHANGES);
            this.hostId = hostId;
            this.problemType = problemType;
            this.detectionDate = detectionDate;
            this.actualSince = actualSince;
            this.problemContent = problemContent;
            this.attempt = attempt;
        }
    }

    public static class MainMirrorChanged extends MessageContent {
        private final WebmasterHostId hostId;
        private final WebmasterHostId oldMirrorId;
        private final WebmasterHostId newMirrorId;

        public MainMirrorChanged(
                @JsonProperty("hostId") WebmasterHostId hostId,
                @JsonProperty("oldMirrorId") WebmasterHostId oldMirrorId,
                @JsonProperty("newMirrorId") WebmasterHostId newMirrorId) {
            super(MessageTypeEnum.MAIN_MIRROR_CHANGED);
            this.hostId = hostId;
            this.oldMirrorId = oldMirrorId;
            this.newMirrorId = newMirrorId;
        }

        public WebmasterHostId getHostId() {
            return hostId;
        }

        public WebmasterHostId getOldMirrorId() {
            return oldMirrorId;
        }

        public WebmasterHostId getNewMirrorId() {
            return newMirrorId;
        }
    }

    @Getter
    public static class AutoAddMainMirror extends MessageContent {
        private final WebmasterHostId hostId;
        private final WebmasterHostId oldMirrorId;
        private final WebmasterHostId newMirrorId;

        public AutoAddMainMirror(
                @JsonProperty("hostId") WebmasterHostId hostId,
                @JsonProperty("oldMirrorId") WebmasterHostId oldMirrorId,
                @JsonProperty("newMirrorId") WebmasterHostId newMirrorId) {
            super(MessageTypeEnum.AUTO_ADD_MAIN_MIRROR);
            this.hostId = hostId;
            this.oldMirrorId = oldMirrorId;
            this.newMirrorId = newMirrorId;
        }


        public String getOldMirror() {
            return IdUtils.toHostString(oldMirrorId, false, true, false);
        }

        public String getNewMirror() {
            return IdUtils.toHostString(newMirrorId, false, true, false);
        }
    }

    @Getter
    public static class NoMainMirrors extends MessageContent {
        private final WebmasterHostId hostId;
        private final WebmasterHostId mainMirrorId;

        public NoMainMirrors(
                @JsonProperty("hostId") WebmasterHostId hostId,
                @JsonProperty("mainMirrorId") WebmasterHostId mainMirrorId) {
            super(MessageTypeEnum.NO_MAIN_MIRRORS);
            this.hostId = hostId;
            this.mainMirrorId = mainMirrorId;
        }


        public String getMainMirror() {
            return IdUtils.toHostString(mainMirrorId, false, true, false);
        }
    }



    public static class SearchBaseUpdate extends MessageContent {
        private final DateTime date;

        public SearchBaseUpdate(@JsonProperty("date") DateTime date) {
            super(MessageTypeEnum.SEARCH_BASE_UPDATE);
            this.date = date;
        }

        public DateTime getDate() {
            return date;
        }
    }

    public static class SearchBaseUpdateNew extends HostMessageContent {
        private final DateTime date;

        private final long totalPages;
        private final long newPages;
        private final long gonePages;
        private final List<String> newUrlSamples;
        private final List<String> goneUrlSamples;

        public SearchBaseUpdateNew(@JsonProperty("date") DateTime date,
                                   @JsonProperty("hostId") WebmasterHostId hostId,
                                   @JsonProperty("totalPages") long totalPages,
                                   @JsonProperty("newPages") long newPages,
                                   @JsonProperty("gonePages") long gonePages,
                                   @JsonProperty("newUrlExamples") List<String> newUrlSamples,
                                   @JsonProperty("goneUrlExamples") List<String> goneUrlSamples) {
            super(MessageTypeEnum.SEARCH_BASE_UPDATE_NEW, hostId);
            this.date = date;
            this.totalPages = totalPages;
            this.newPages = newPages;
            this.gonePages = gonePages;
            this.newUrlSamples = newUrlSamples;
            this.goneUrlSamples = goneUrlSamples;
        }

        private static String renderLinksToHtml(List<String> urls) {
            return urls == null ? "" : urls.stream()
                    .map(StringEscapeUtils::escapeHtml4)
                    .map(l -> String.format("<a href=\"%s\" rel=\"noopener nofollow noreferrer\" class=\"link link_theme_normal\">%s</a>", l, l))
                    .collect(Collectors.joining("<br/>"));
        }

        @Override
        public boolean isRequiresHost() {
            return getHostId() != null;
        }

        public boolean hasUpdates() {
            return newPages + gonePages > 0;
        }

        public DateTime getDate() {
            return date;
        }

        public long getTotalPages() {
            return totalPages;
        }

        public long getNewPages() {
            return newPages;
        }

        public long getGonePages() {
            return gonePages;
        }

        public List<String> getNewUrlSamples() {
            return newUrlSamples;
        }

        public String getNewUrlExamplesHtml() {
            return renderLinksToHtml(newUrlSamples);
        }

        public List<String> getGoneUrlSamples() {
            return goneUrlSamples;
        }

        public String getGoneUrlExamplesHtml() {
            return renderLinksToHtml(goneUrlSamples);
        }

        @Override
        public String toString() {
            return MoreObjects.toStringHelper(this)
                    .add("hostId", getHostId())
                    .add("date", date)
                    .add("totalPages", totalPages)
                    .add("newPages", newPages)
                    .add("gonePages", gonePages)
                    .add("newUrlSamples", newUrlSamples)
                    .add("goneUrlSamples", goneUrlSamples)
                    .toString();
        }
    }

    public static class SearchBaseUpdateNewAllUserHosts extends MessageContent {
        private final DateTime date;
        private final List<SearchBaseUpdateNew> hostsInfo;

        public SearchBaseUpdateNewAllUserHosts(
                @JsonProperty("date") DateTime date,
                @JsonProperty("hostsInfo") List<SearchBaseUpdateNew> hostsInfo) {
            super(MessageTypeEnum.SEARCH_BASE_UPDATE_ALL_USER_HOSTS);
            this.date = date;
            this.hostsInfo = hostsInfo;
        }

        public DateTime getDate() {
            return date;
        }

        public List<SearchBaseUpdateNew> getHostsInfo() {
            return hostsInfo;
        }

        public static String getHostsHtml(List<SearchBaseUpdateNew> infos) {
            List<WebmasterHostId> hostsWithoutUpdates = infos.stream()
                    .filter(info -> !info.hasUpdates())
                    .map(HostMessageContent::getHostId)
                    .collect(Collectors.toList());
            return renderLinksToHtml(hostsWithoutUpdates.stream().map(hostId -> IdUtils.toHostString(hostId, false, true, false)).collect(Collectors.toList()));
        }

        private static String renderLinksToHtml(List<String> urls) {
            return urls == null ? "" : urls.stream()
                    .map(StringEscapeUtils::escapeHtml4)
                    .map(l -> String.format("<a href=\"%s\" rel=\"noopener nofollow noreferrer\">%s</a>", l, l))
                    .collect(Collectors.joining("<br/>"));
        }

        public static String linkToHtml(String url) {
            String escapedUrl = StringEscapeUtils.escapeHtml4(url);
            return String.format("<a href=\"%s\" rel=\"noopener nofollow noreferrer\">%s</a>", escapedUrl, escapedUrl);
        }
    }

    public static class HostAccessDelegatedSomeoneElse extends HostMessageContent {
        public HostAccessDelegatedSomeoneElse(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.HOST_ACCESS_DELEGATED_SOMEONE_ELSE, hostId);
        }
    }

    public static class AutoAddedImportantUrls extends HostMessageContent {
        private final List<String> urls;

        public AutoAddedImportantUrls(
                @JsonProperty("hostId") WebmasterHostId hostId,
                @JsonProperty("urls") List<String> urls) {
            super(MessageTypeEnum.IMPORTANT_URLS_AUTO_ADD, hostId);
            this.urls = Preconditions.checkNotNull(urls);
        }

        public List<String> getUrls() {
            return urls;
        }

        @JsonIgnore
        public String renderAddedUrlsHtml() {
            return urls.stream()
                    .map(d -> String.format("<li>%s</li>", d))
                    .collect(Collectors.joining("\n"));
        }
    }

    public static class UnverifiedHostRemovalAfterWeek extends HostMessageContent {
        public UnverifiedHostRemovalAfterWeek(
                @JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.UNVERIFIED_HOST_REMOVAL_AFTER_WEEK, hostId);
        }
    }

    public static class UnverifiedHostRemovalAfterThreeWeek extends HostMessageContent {
        public UnverifiedHostRemovalAfterThreeWeek(
                @JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.UNVERIFIED_HOST_REMOVAL_AFTER_THREE_WEEK, hostId);
        }
    }

    public static class HostAccessDelegatedToUser extends MessageContent {
        public HostAccessDelegatedToUser() {
            super(MessageTypeEnum.HOST_ACCESS_DELEGATED_TO_USER);
        }
    }

    public static class HostAccessDelegatedToNotUser extends MessageContent {
        private final String token;

        public HostAccessDelegatedToNotUser(@JsonProperty("token") String token) {
            super(MessageTypeEnum.HOST_ACCESS_DELEGATED_TO_NOT_USER);
            this.token = token;
        }

        public String getToken() {
            return token;
        }
    }

    public static class HostRegionChanged extends HostMessageContent {
        public HostRegionChanged(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.HOST_REGION_CHANGED, hostId);
        }
    }

    public static class HostRegionChangeCancelled extends HostMessageContent {
        public HostRegionChangeCancelled(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.HOST_REGION_CHANGE_CANCELLED, hostId);
        }
    }

    public static class HostTrendOpened extends HostMessageContent {
        public HostTrendOpened(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.HOST_TREND_OPENED, hostId);
        }
    }

    public static class HostRecommendedOpened extends HostMessageContent {
        public HostRecommendedOpened(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.HOST_RECOMMENDED_OPENED, hostId);
        }
    }

    public static class HostAccessLost extends HostMessageContent {
        public HostAccessLost(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.HOST_ACCESS_LOST, hostId);
        }
    }

    public static class HostRecommendedClosed extends HostMessageContent {
        private final DateTime date;
        private final DateTime date2;

        public HostRecommendedClosed(@JsonProperty("hostId") WebmasterHostId hostId,
                                     @JsonProperty("date") DateTime date,
                                     @JsonProperty("date2") DateTime date2) {
            super(MessageTypeEnum.HOST_RECOMMENDED_CLOSED, hostId);
            this.date = date;
            this.date2 = date2;
        }

        public DateTime getDate() {
            return date;
        }

        public DateTime getDate2() {
            return date2;
        }
    }

    public static class HostDisplayNameChanged extends HostMessageContent {
        public HostDisplayNameChanged(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.HOST_DISPLAY_NAME_CHANGED, hostId);
        }
    }

    public static class HostDisplayNameChangeCancelled extends HostMessageContent {
        public HostDisplayNameChangeCancelled(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.HOST_DISPLAY_NAME_CHANGE_CANCELLED, hostId);
        }
    }

    public static class HostMetrikaCounterBindingRequest extends HostMessageContent {
        private final long counterId;

        public HostMetrikaCounterBindingRequest(@JsonProperty("hostId") WebmasterHostId hostId,
                                                @JsonProperty("counterId") long counterId) {
            super(MessageTypeEnum.HOST_METRIKA_COUNTER_BINDING_REQUEST, hostId);
            this.counterId = counterId;
        }

        public long getCounterId() {
            return counterId;
        }
    }

    public static class HostMetrikaCounterBindingRequestCancelled extends HostMessageContent {
        private final long counterId;

        public HostMetrikaCounterBindingRequestCancelled(@JsonProperty("hostId") WebmasterHostId hostId,
                                                         @JsonProperty("counterId") long counterId) {
            super(MessageTypeEnum.HOST_METRIKA_COUNTER_BINDING_REQUEST_CANCELLED, hostId);
            this.counterId = counterId;
        }

        public long getCounterId() {
            return counterId;
        }
    }

    public static class HostMetrikaCounterBindingDeleted extends HostMessageContent {
        private final long counterId;
        private final String user;

        public HostMetrikaCounterBindingDeleted(@JsonProperty("hostId") WebmasterHostId hostId,
                                                @JsonProperty("counterId") long counterId,
                                                @JsonProperty("user") String user) {
            super(MessageTypeEnum.HOST_METRIKA_COUNTER_BINDING_DELETED, hostId);
            this.counterId = counterId;
            this.user = user;
        }

        public long getCounterId() {
            return counterId;
        }

        public String getUser() {
            return user;
        }
    }

    public static class HostCompanyProfileNotFilled extends HostMessageContent {
        private final String companyName;
        private final long tycoonId;

        public HostCompanyProfileNotFilled(@JsonProperty("hostId") WebmasterHostId hostId,
                                           @JsonProperty("companyName") String companyName,
                                           @JsonProperty("tycoonId") long tycoonId) {
            super(MessageTypeEnum.HOST_COMPANY_PROFILE_NOT_FILLED, hostId);
            this.companyName = companyName;
            this.tycoonId = tycoonId;
        }

        public String getCompanyName() {
            return companyName;
        }

        public long getTycoonId() {
            return tycoonId;
        }
    }

    public static class TurboAutoparsedPagesAppeared extends HostMessageContent {
        public TurboAutoparsedPagesAppeared(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.TURBO_AUTOPARSED_PAGES_APPEARED, hostId);
        }
    }

    public static class RobotsTxtChange extends HostMessageContent {
        private final DateTime date;

        public RobotsTxtChange(@JsonProperty("hostId") WebmasterHostId hostId,
                               @JsonProperty("date") DateTime date) {
            super(MessageTypeEnum.ROBOTS_TXT_CHANGE, hostId);
            this.date = date;
        }

        public DateTime getDate() {
            return date;
        }
    }

    public static class NewDomainsNotification extends HostMessageContent {
        private final List<String> domains;

        public NewDomainsNotification(
                @JsonProperty("hostId") WebmasterHostId hostId,
                @JsonProperty("domains") List<String> domains) {
            super(MessageTypeEnum.NEW_DOMAINS_NOTIFICATION, hostId);
            this.domains = Preconditions.checkNotNull(domains);
        }

        public List<String> getDomains() {
            return domains;
        }

        @JsonIgnore
        public String renderNewDomainsHtml() {
            String listElements = domains.stream()
                    .map(d -> String.format("<li>%s</li>", d))
                    .collect(Collectors.joining("\n"));
            return String.format("<ul>%s</ul>", listElements);
        }
    }

    public static class HostMetrikaCounterCrawlSuspended extends HostMessageContent {
        private final long counterId;

        @JsonCreator
        public HostMetrikaCounterCrawlSuspended(@JsonProperty("hostId") WebmasterHostId hostId,
                                                @JsonProperty("counterId") long counterId) {
            super(MessageTypeEnum.HOST_METRIKA_COUNTER_CRAWL_SUSPENDED, hostId);
            this.counterId = counterId;
        }

        public long getCounterId() {
            return counterId;
        }
    }

    public static class HostMetrikaCounterCrawlSamplesUpdated extends HostMessageContent {
        private final long counterId;

        @JsonCreator
        public HostMetrikaCounterCrawlSamplesUpdated(@JsonProperty("hostId") WebmasterHostId hostId,
                                                     @JsonProperty("counterId") long counterId) {
            super(MessageTypeEnum.HOST_METRIKA_COUNTER_CRAWL_SUSPENDED, hostId);
            this.counterId = counterId;
        }

        public long getCounterId() {
            return counterId;
        }
    }

    public static class TurboListingsAvailable extends HostMessageContent {
        public TurboListingsAvailable(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.TURBO_LISTINGS_AVAILABLE, hostId);
        }
    }

    @Getter
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class TurboAdvSettingsChanged extends HostMessageContent {

        private final TurboPlatform platform;
        private final AdvertisingPlacement placement;

        public TurboAdvSettingsChanged(@JsonProperty("hostId") WebmasterHostId hostId,
                                       @JsonProperty("platform") TurboPlatform platform,
                                       @JsonProperty("placement") AdvertisingPlacement placement) {
            super(MessageTypeEnum.TURBO_ADV_SETTINGS_CHANGE, hostId);
            this.platform = platform;
            this.placement = placement;
        }

        public static TurboAdvSettingsChanged create(WebmasterHostId hostId,
                                                     Collection<AdvertisingPlacement> changedMobileAdPlacements,
                                                     Collection<AdvertisingPlacement> changedDesktopAdPlacements) {
            TurboPlatform platform = !CollectionUtils.isEmpty(changedMobileAdPlacements) ? TurboPlatform.MOBILE : TurboPlatform.DESKTOP;
            AdvertisingPlacement placement = platform == TurboPlatform.MOBILE ? changedMobileAdPlacements.iterator().next() :
                    !CollectionUtils.isEmpty(changedDesktopAdPlacements) ? changedDesktopAdPlacements.iterator().next() : AdvertisingPlacement.TOP;
            return new TurboAdvSettingsChanged(hostId, platform, placement);
        }
    }

    @Getter
    // пока только оплаты
    public static class TurboCommerceSettingsChanged extends HostMessageContent {

        public TurboCommerceSettingsChanged(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.TURBO_COMMERCE_SETTINGS_CHANGE, hostId);
        }
    }

    public static class NewReviewAvailable extends HostMessageContent {
        private List<String> list;
        private int countPositive;
        private int countNegative;
        private String renderType;

        public NewReviewAvailable(@JsonProperty("hostId") WebmasterHostId hostId,
                                  @JsonProperty("list") List<String> list,
                                  @JsonProperty("countPositive") int countPositive,
                                  @JsonProperty("countNegative") int countNegative,
                                  @JsonProperty("renderType") String renderType) {
            super(MessageTypeEnum.NEW_REVIEW, hostId);
            this.list = list;
            this.countPositive = countPositive;
            this.countNegative = countNegative;
            this.renderType = renderType;
        }

        public List<String> getList() {
            return list;
        }

        public int getCountPositive() {
            return countPositive;
        }

        public int getCountNegative() {
            return countNegative;
        }

        public String getRenderType() {
            return renderType;
        }
    }

    public static class IksNewInfo extends HostMessageContent {
        private IksMessageContent iksMessageContent;
        private IksMessageType messageType;
        private String groupType;
        private List<WebmasterHostId> hostIds;
        private List<Long> iksValues;


        public IksNewInfo(@JsonProperty("hostIds") List<WebmasterHostId> hostIds,
                          @JsonProperty("iksValues") List<Long> iksValues,
                          @JsonProperty("hostId") WebmasterHostId hostId,
                          @JsonProperty("iksUpdateMessageContent") IksMessageContent iksMessageContent,
                          @JsonProperty("messageType") IksMessageType messageType,
                          @JsonProperty("groupType") String groupType) {
            super(MessageTypeEnum.IKS_UPDATE, hostId);
            this.iksMessageContent = iksMessageContent;
            this.messageType = messageType;
            this.groupType = groupType;
            this.hostIds = hostIds;
            this.iksValues = iksValues;
        }


        public IksMessageContent getIksMessageContent() {
            return iksMessageContent;
        }

        public IksMessageType getMessageType() {
            return messageType;
        }

        public String getGroupType() {
            return groupType;
        }


        public List<WebmasterHostId> getHostIds() {
            return hostIds;
        }

        public List<Long> getIksValues() {
            return iksValues;
        }


    }

    @Getter
    public static class TurboSccPass extends HostMessageContent {
        private final WebmasterHostId hostId;

        public TurboSccPass(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.TURBO_SCC_PASS, hostId);
            this.hostId = hostId;
        }
    }

    @Getter
    public static class TurboSccFailed extends HostMessageContent {
        private final WebmasterHostId hostId;

        public TurboSccFailed(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.TURBO_SCC_FAILED, hostId);
            this.hostId = hostId;
        }
    }

    @Getter
    public static class TurboSccBanned extends HostMessageContent {
        private final WebmasterHostId hostId;

        public TurboSccBanned(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.TURBO_SCC_BANNED, hostId);
            this.hostId = hostId;
        }
    }

    @Getter
    public static class TurboSccUnbanned extends HostMessageContent {
        private final WebmasterHostId hostId;

        public TurboSccUnbanned(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.TURBO_SCC_UNBANNED, hostId);
            this.hostId = hostId;
        }
    }

    @Getter
    public static class VideohostOfferConfirmed extends HostMessageContent {
        private final String offerUrl;

        @JsonCreator
        public VideohostOfferConfirmed(@JsonProperty("hostId") WebmasterHostId hostId,
                                       @JsonProperty("offerUrl") String offerUrl) {
            super(MessageTypeEnum.VIDEOHOST_OFFER_CONFIRMED, hostId);
            this.offerUrl = offerUrl;
        }
    }

    public static class VideohostOfferHasPaper extends HostMessageContent {
        @JsonCreator
        public VideohostOfferHasPaper(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.VIDEOHOST_OFFER_HAS_PAPER, hostId);
        }
    }

    public static class VideohostOfferRedundant extends HostMessageContent {
        @JsonCreator
        public VideohostOfferRedundant(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.VIDEOHOST_OFFER_REDUNDANT, hostId);
        }
    }

    @Getter
    public static class FeedsSccFailed extends HostMessageContent {
        private final List<String> feedUrls;
        private final int attempt;

        public FeedsSccFailed(WebmasterHostId hostId, String feedUrl) {
            this(hostId, Collections.singletonList(feedUrl), 0);
        }

        @JsonCreator
        public FeedsSccFailed(@JsonProperty("hostId") WebmasterHostId hostId,
                              @JsonProperty("feedUrls") List<String> feedUrls,
                              @JsonProperty("attempt") int attempt) {
            super(MessageTypeEnum.FEEDS_SCC_FAILED, hostId);
            this.feedUrls = feedUrls;
            this.attempt = attempt;
        }
    }

    @Getter
    public static class FeedsSccBanned extends HostMessageContent {
        private final List<String> feedUrls;

        public FeedsSccBanned(WebmasterHostId hostId, String feedUrl) {
            this(hostId, Collections.singletonList(feedUrl));
        }

        @JsonCreator
        public FeedsSccBanned(@JsonProperty("hostId") WebmasterHostId hostId,
                              @JsonProperty("feedUrls") List<String> feedUrls) {
            super(MessageTypeEnum.FEEDS_SCC_BANNED, hostId);
            this.feedUrls = feedUrls;
        }
    }

    @Getter
    public static class FeedsSccPassed extends HostMessageContent {
        private final List<String> feedUrls;

        @JsonCreator
        public FeedsSccPassed(@JsonProperty("hostId") WebmasterHostId hostId,
                              @JsonProperty("feedUrls") List<String> feedUrls) {
            super(MessageTypeEnum.FEEDS_SCC_PASSED, hostId);
            this.feedUrls = feedUrls;
        }
    }

    @Getter
    public static class FeedsPingerDisable extends HostMessageContent {
        private final String feedUrl;

        @JsonCreator
        public FeedsPingerDisable(@JsonProperty("hostId") WebmasterHostId hostId,
                                  @JsonProperty("feedUrl") String feedUrl) {
            super(MessageTypeEnum.FEEDS_PINGER_DISABLE, hostId);
            this.feedUrl = feedUrl;
        }
    }

    @Getter
    public static class FeedsPingerEnable extends HostMessageContent {
        private final String feedUrl;

        @JsonCreator
        public FeedsPingerEnable(@JsonProperty("hostId") WebmasterHostId hostId,
                                 @JsonProperty("feedUrl") String feedUrl) {
            super(MessageTypeEnum.FEEDS_PINGER_ENABLE, hostId);
            this.feedUrl = feedUrl;
        }
    }

    @Getter
    public static class FeedsDefectRateFailed extends HostMessageContent {
        private final FeedsErrorSeverity severity;

        @JsonCreator
        public FeedsDefectRateFailed(@JsonProperty("hostId") WebmasterHostId hostId,
                                     @JsonProperty("severity") FeedsErrorSeverity severity) {
            super(MessageTypeEnum.FEEDS_DEFECT_RATE_FAILED, hostId);
            this.severity = severity;
        }
    }

    @Getter
    public static class FeedsValidationFailed extends HostMessageContent {
        private final List<String> feedUrls;
        private final int attempt;

        @JsonCreator
        public FeedsValidationFailed(@JsonProperty("hostId") WebmasterHostId hostId,
                                     @JsonProperty("feedUrls") List<String> feedUrls,
                                     @JsonProperty("attempt") int attempt) {
            super(MessageTypeEnum.FEEDS_VALIDATION_FAILED, hostId);
            this.feedUrls = feedUrls;
            this.attempt = attempt;
        }
    }

    @Getter
    public static class NcaCertificate extends HostMessageContent {

        @JsonCreator
        public NcaCertificate(@JsonProperty("hostId") WebmasterHostId hostId) {
            super(MessageTypeEnum.NCA_CERTIFICATE, hostId);
        }
    }

    @Getter
    public static class NcaCtlogCertificate extends HostMessageContent {
        private final String domain;
        private final String ctlog;
        private final int ctlogRow;

        @JsonCreator
        public NcaCtlogCertificate(
                @JsonProperty("hostId") WebmasterHostId hostId,
                @JsonProperty("ctlog") String ctlog,
                @JsonProperty("ctlogRow") int ctlogRow
        ) {
            super(MessageTypeEnum.NCA_CTLOG_CERTIFICATE, hostId);
            this.domain = hostId.getPunycodeHostname();
            this.ctlog = ctlog;
            this.ctlogRow = ctlogRow;
        }
    }
}
