package ru.yandex.webmaster3.worker.digest.html;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.Value;
import org.jetbrains.annotations.NotNull;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;

import ru.yandex.common.util.collections.Pair;
import ru.yandex.webmaster3.core.checklist.data.SiteProblemTypeEnum;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.storage.achievements.model.AchievementTld;
import ru.yandex.webmaster3.storage.achievements.model.AchievementType;
import ru.yandex.webmaster3.storage.achievements.model.official.AchievementOfficialType;

import java.util.List;
import java.util.Optional;

/**
 * Created by ifilippov5 on 25.09.17.
 */
public class DigestData {
    private final Host host;
    private final LocalDate digestFrom;
    private final LocalDate digestTo;
    private final Optional<Checklist> checklist;
    private final Optional<Searchable> searchable;
    private final Optional<Clicks> clicks;
    private final Optional<Queries> queries;
    private final Optional<BlogPosts> blogPosts;
    private final Optional<QueriesGroups> queriesGroups;
    private final Optional<ImportantUrls> importantUrl;
    private final Optional<RecommendedUrls> recommendedUrl;
    private final Optional<List<Pair<AchievementTld, List<AbstractAchievement>>>> achievements;
    private final Optional<Reviews> reviews;
    private final Optional<Mirrors> mirrors;
    private final Optional<MetrikaCounterProblem> metrikaCounterProblem;
    private final Optional<Boolean> isFirstLiteDigest;

    public DigestData(@NotNull Host host, @NotNull LocalDate digestFrom, @NotNull LocalDate digestTo, Optional<Checklist> checklist,
                      Optional<Searchable> searchable, Optional<Clicks> clicks, Optional<Queries> queries, Optional<QueriesGroups> queriesGroups,
                      Optional<ImportantUrls> importantUrl, Optional<RecommendedUrls> recommendedUrl, Optional<BlogPosts> blogPosts,
                      Optional<List<Pair<AchievementTld, List<AbstractAchievement>>>> achievements, Optional<Reviews> reviews, Optional<Mirrors> mirrors,
                      Optional<MetrikaCounterProblem> metrikaCounterProblem, Optional<Boolean> isFirstLiteDigest) {
        this.host = host;
        this.digestFrom = digestFrom;
        this.digestTo = digestTo;
        this.checklist = checklist;
        this.searchable = searchable;
        this.clicks = clicks;
        this.queries = queries;
        this.queriesGroups = queriesGroups;
        this.importantUrl = importantUrl;
        this.recommendedUrl = recommendedUrl;
        this.blogPosts = blogPosts;
        this.achievements = achievements;
        this.reviews = reviews;
        this.mirrors = mirrors;
        this.metrikaCounterProblem = metrikaCounterProblem;
        this.isFirstLiteDigest = isFirstLiteDigest;
    }

    public Host getHost() {
        return host;
    }

    public LocalDate getDigestFrom() {
        return digestFrom;
    }

    public LocalDate getDigestTo() {
        return digestTo;
    }

    public LocalDate getPrevWeekFrom() {
        return digestFrom.minusWeeks(1);
    }

    public LocalDate getPrevWeekTo() {
        return digestTo.minusWeeks(1);
    }

    public Optional<Checklist> getChecklist() {
        return checklist;
    }

    public Optional<Searchable> getSearchable() {
        return searchable;
    }

    public Optional<Clicks> getClicks() {
        return clicks;
    }

    public Optional<Queries> getQueries() {
        return queries;
    }

    public Optional<QueriesGroups> getQueriesGroups() {
        return queriesGroups;
    }

    public Optional<ImportantUrls> getImportantUrl() {
        return importantUrl;
    }

    public Optional<RecommendedUrls> getRecommendedUrl() {
        return recommendedUrl;
    }

    public Optional<List<Pair<AchievementTld, List<AbstractAchievement>>>> getAchievements() {
        return achievements;
    }

    public Optional<BlogPosts> getBlogPosts() {
        return blogPosts;
    }

    public Optional<Reviews> getReviews() {
        return reviews;
    }

    public Optional<Mirrors> getMirrors() {
        return mirrors;
    }

    public Optional<MetrikaCounterProblem> getMetrikaCounterProblem() {
        return metrikaCounterProblem;
    }

    public Optional<Boolean> getIsFirstLiteDigest() {
        return isFirstLiteDigest;
    }

    public static class Host {
        private final WebmasterHostId id;
        private final String displayName;

        public Host(WebmasterHostId id, String displayName) {
            this.id = id;
            this.displayName = displayName;
        }

        public WebmasterHostId getId() {
            return id;
        }

        public String getDisplayName() {
            return displayName;
        }
    }

    public static class Checklist {
        private final List<SiteProblemTypeEnum> presentProblems;
        private final boolean problemsOnlyInTurbo;
        private final boolean problemsOnlyInTurboContent;
        private final List<SiteProblemTypeEnum> fixedProblems;
        private final List<HostEvent> events;

        public Checklist(List<SiteProblemTypeEnum> presentProblems, boolean problemsOnlyInTurbo, boolean problemsOnlyInTurboContent, List<SiteProblemTypeEnum> fixedProblems, List<HostEvent> events) {
            this.presentProblems = presentProblems;
            this.problemsOnlyInTurbo = problemsOnlyInTurbo;
            this.problemsOnlyInTurboContent = problemsOnlyInTurboContent;
            this.fixedProblems = fixedProblems;
            this.events = events;
        }

        public List<SiteProblemTypeEnum> getPresentProblems() {
            return presentProblems;
        }

        public boolean isProblemsOnlyInTurbo() {
            return problemsOnlyInTurbo;
        }

        public boolean isProblemsOnlyInTurboContent() {
            return problemsOnlyInTurboContent;
        }

        public List<SiteProblemTypeEnum> getFixedProblems() {
            return fixedProblems;
        }

        public List<HostEvent> getEvents() {
            return events;
        }
    }

    @Getter
    @AllArgsConstructor
    public static class Searchable {
        private final long prevCount;
        private final long count;
        private final Stats stats;
        private final List<Sample> samples;

        Difference getDifference() {
            if (prevCount == 0) {
                return null;
            }

            return new Difference(((count - prevCount) * 100) / prevCount);
        }

        public static class Stats {
            private final Value added;
            private final Value removed;

            public Stats(Long added, Long removed) {
                this.added = new Value(added);
                this.removed = new Value(removed);
            }

            public Value getAdded() {
                return added;
            }

            public Value getRemoved() {
                return removed;
            }

            public static class Value {
                private final Long view;
                private final long value;

                public Value(Long value) {
                    this.view = value;
                    this.value = value == null ? 0L : value;
                }

                public Long getView() {
                    return view;
                }

                public long getValue() {
                    return value;
                }
            }
        }

        public static class Sample {
            private final String title;
            private final String url;
            private final StatsEnum status;
            private String reason;

            public Sample(String title, String url, StatsEnum status) {
                this.title = title;
                this.url = url;
                this.status = status;
            }

            public Sample(String title, String url, StatsEnum status, String reason) {
                this(title, url, status);
                this.reason = reason;
            }

            public String getTitle() {
                return title;
            }

            public String getUrl() {
                return url;
            }

            public StatsEnum getStatus() {
                return status;
            }

            public String getReason() {
                return reason;
            }
        }
    }

    public static class Clicks {
        private final long count;
        private final Optional<Long> pastWeekylClicksCount;

        public Clicks(long count, Optional<Long> pastWeekylClicksCount) {
            this.count = count;
            this.pastWeekylClicksCount = pastWeekylClicksCount;
        }

        public long getCount() {
            return count;
        }

        public Optional<Long> getPastWeekylClicksCount() {
            return pastWeekylClicksCount;
        }
    }

    public static class Queries {
        private final List<QData> data;

        public Queries(List<QData> data) {
            this.data = data;
        }

        public List<QData> getData() {
            return data;
        }

        public static class QData {
            private final String query;
            private final Long clicks;
            private final Difference difference;

            public QData(String query, Long clicks, Long difference) {
                this.query = query;
                this.clicks = clicks;
                this.difference = new Difference(difference);
            }

            public String getQuery() {
                return query;
            }

            public Long getClicks() {
                return clicks;
            }

            public Difference getDifference() {
                return difference;
            }
        }
    }

    public static class QueriesGroups {
        private final List<QDataGroups> data;

        public QueriesGroups(List<QDataGroups> data) {
            this.data = data;
        }

        public List<QDataGroups> getData() {
            return data;
        }

        public static class QDataGroups {
            private final String title;
            private final Long clicks;
            private final Difference difference;

            public QDataGroups(String title, Long clicks, Long difference) {
                this.title = title;
                this.clicks = clicks;
                this.difference = new Difference(difference);
            }

            public String getTitle() {
                return title;
            }

            public Long getClicks() {
                return clicks;
            }

            public Difference getDifference() {
                return difference;
            }
        }
    }

    public static class Difference {
        private final Long value;

        public Difference(Long difference) {
            value = difference;
        }

        public Long getValue() {
            return value;
        }
    }

    public static class ImportantUrl {
        private final String url;
        private final String title;
        private final HighlightedValue indexingHttpCode;
        private final HighlightedValue searchUrlStatus;

        public ImportantUrl(String url, String title, HighlightedValue indexingHttpCode, HighlightedValue searchUrlStatus) {
            this.title = title;
            this.url = url;
            this.indexingHttpCode = indexingHttpCode;
            this.searchUrlStatus = searchUrlStatus;
        }

        public String getUrl() {
            return url;
        }

        public String getTitle() {
            return title;
        }

        public HighlightedValue getIndexingHttpCode() {
            return indexingHttpCode;
        }

        public HighlightedValue getSearchUrlStatus() {
            return searchUrlStatus;
        }

        public static class HighlightedValue {
            private final String value;
            private final boolean highlight;

            public HighlightedValue(String value, boolean highlight) {
                this.value = value;
                this.highlight = highlight;
            }

            public String getValue() {
                return value;
            }

            public boolean isHighlight() {
                return highlight;
            }
        }
    }

    public static class RecommendedUrl {
        private final String url;
        private final Long shows;
        private final Long clicks;

        public RecommendedUrl(String url, Long shows, Long clicks) {
            this.url = url;
            this.shows = shows;
            this.clicks = clicks;
        }

        public String getUrl() {
            return url;
        }

        public Long getShows() {
            return shows;
        }

        public Long getClicks() {
            return clicks;
        }
    }

    public static class ImportantUrls {
        private final List<ImportantUrl> importantUrls;

        public ImportantUrls(List<ImportantUrl> importantUrls) {
            this.importantUrls = importantUrls;
        }

        public List<ImportantUrl> getImportantUrls() {
            return importantUrls;
        }
    }

    public static class RecommendedUrls {
        private final List<RecommendedUrl> recommendedUrls;

        public RecommendedUrls(List<RecommendedUrl> recommendedUrls) {
            this.recommendedUrls = recommendedUrls;
        }

        public List<RecommendedUrl> getRecommendedUrls() {
            return recommendedUrls;
        }
    }

    public static class Reviews {
        private final long delta;
        private final long prevCount;

        public Reviews(long delta, long prevCount) {
            this.delta = delta;
            this.prevCount = prevCount;
        }

        public long getDelta() {
            return delta;
        }
    }

    public static class Mirrors {
        private final String MainHost;

        public Mirrors(String MainHost) {
            this.MainHost = MainHost;
        }

        public String getMainHost() {
            return MainHost;
        }
    }

    @Value
    public static class MetrikaCounterProblem {
        SiteProblemTypeEnum problemType;
    }

    public static class BlogPost {
        private final String slug;
        private final String approvedTitle;
        private final DateTime publishDate;

        public BlogPost(String slug, String approvedTitle, DateTime publishDate) {
            this.slug = slug;
            this.approvedTitle = approvedTitle;
            this.publishDate = publishDate;
        }

        public String getSlug() {
            return slug;
        }

        public String getTitle() {
            return approvedTitle;
        }

        public DateTime getDate() {
            return publishDate;
        }
    }

    public static class BlogPosts {
        private final List<BlogPost> blogPosts;

        public BlogPosts(List<BlogPost> blogPosts) {
            this.blogPosts = blogPosts;
        }

        public List<BlogPost> getBlogPosts() {
            return blogPosts;
        }
    }

    public enum StatsEnum {
        ADDED("added"),
        REMOVED("removed"),;

        private final String value;

        StatsEnum(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }
    }

    public static abstract class AbstractAchievement {
        public final AchievementTld domain;

        public AbstractAchievement(AchievementTld domain) {
            this.domain = domain;
        }
    }

    public static class Achievement extends AbstractAchievement {
        public final AchievementType type;

        public Achievement(AchievementTld domain, AchievementType type) {
            super(domain);
            this.type = type;
        }
    }

    public static class AchievementOfficial extends AbstractAchievement {
        public final AchievementOfficialType type;

        public AchievementOfficial(AchievementTld domain, AchievementOfficialType type) {
            super(domain);
            this.type = type;
        }
    }

    public static class BrandedOfficialAchievement extends AchievementOfficial {
        public final String brand;

        public BrandedOfficialAchievement(AchievementTld domain, AchievementOfficialType type, String brand) {
            super(domain, type);
            this.brand = brand;
        }
    }

    public static abstract class HostEvent {}

    public static class RecrawledPages extends HostEvent {
        public final long count;

        public RecrawledPages(long count) {
            this.count = count;
        }
    }

    public static class RegionChanged extends HostEvent {}

    public static class HostDisplayNameChanged extends HostEvent {}

    @Getter
    @AllArgsConstructor
    public static class YmlTurboAvailable extends HostEvent {
        private final WebmasterHostId hostId;
    }

    public static class UserVerifiedHost extends HostEvent {
        public final String login;

        public UserVerifiedHost(String login) {
            this.login = login;
        }
    }

    public static class AutoparserOff extends HostEvent {
        public final long pagesCount;

        public AutoparserOff(long pagesCount) {
            this.pagesCount = pagesCount;
        }
    }

    public static abstract class IksEvent extends HostEvent {
        public final long iksValue;
        public final String owner;

        public IksEvent(long iksValue, String owner) {
            this.iksValue = iksValue;
            this.owner = owner;
        }
    }

    public static class IksUpdated extends IksEvent {
        public IksUpdated(long iksValue, String owner) {
            super(iksValue, owner);
        }
    }
}
