package io.delphiplatform.api.v3.view.util;

import java.time.LocalDate;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.v3.constant.ApplicationConstants;
import io.delphiplatform.api.v3.model.AggBy;
import io.delphiplatform.api.v3.model.AnalyticsMetrics;
import io.delphiplatform.api.v3.model.ChartTrackStateIncludes;
import io.delphiplatform.api.v3.model.GroupByField;
import io.delphiplatform.api.v3.model.IncludeChart;
import io.delphiplatform.api.v3.model.IncludeStreams;
import io.delphiplatform.api.v3.model.IncludeTracks;
import io.delphiplatform.api.v3.model.IndexParam;
import io.delphiplatform.api.v3.model.JunoSpotifyStreamsIncludes;
import io.delphiplatform.api.v3.model.SortOrder;
import io.delphiplatform.api.v3.model.SubsetParam;
import io.delphiplatform.api.v3.model.TrackMediaType;
import io.delphiplatform.api.v3.model.TrackStateIncludes;
import io.delphiplatform.api.v3.model.TracksGroupByField;
import io.delphiplatform.api.v3.model.ads.AdsDspSlug;
import io.delphiplatform.api.v3.model.ads.GroupByAdsPerformance;
import io.delphiplatform.api.v3.model.datahealth.DataHealthBreakdown;
import io.delphiplatform.api.v3.model.datahealth.DataHealthBreakdownGroup;
import io.delphiplatform.api.v3.model.datahealth.DataHealthStatus;
import io.delphiplatform.api.v3.model.spotify.ChartTrackMetricsDimension;
import io.delphiplatform.api.v3.model.spotify.SpotifyChartBreakdown;
import io.delphiplatform.api.v3.model.spotify.SpotifyChartType;
import io.delphiplatform.api.v3.model.tiktok.TikTokBreakdown;
import io.delphiplatform.api.v3.model.tiktok.TikTokChartType;
import io.delphiplatform.api.v3.model.tiktok.TikTokContentType;
import io.delphiplatform.api.v3.model.tiktok.TikTokMetric;
import io.delphiplatform.api.v3.model.tiktok.TikTokStateMetric;
import io.delphiplatform.api.v3.model.tiktok.TikTokTopAnalyticMetric;
import io.delphiplatform.api.v3.model.tiktok.top.video.TikTokTopIsrcVideoTopBy;
import io.delphiplatform.api.v3.model.video.ExpandTo;
import io.delphiplatform.api.v3.model.video.GroupByVideo;
import io.delphiplatform.api.v3.model.video.IncludeVideoChart;
import io.delphiplatform.api.v3.model.video.VideoChartGroup;
import io.delphiplatform.api.v3.model.video.VideoDspSlug;
import io.delphiplatform.api.v3.model.video.YouTubeMetricsCategory;
import io.delphiplatform.api.v3.model.video.YoutubeContentType;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;

@Getter
@Builder(toBuilder = true)
@ToString
@EqualsAndHashCode
public class Params {

    private final AggBy aggBy;
    private final String artistId;
    private final Set<String> additionalArtistIds;
    private final String channelId;
    private final Set<String> videoIds;
    private final Set<YoutubeContentType> youTubeContentTypes;
    private final Long minPremiumUgcViews;
    private final String chartGroup;
    private final VideoChartGroup videoChartGroup;
    private final List<String> chartId;
    private final Set<String> countryCode;
    private final Set<String> appleCountryCode;
    private final Set<String> amazonCountryCode;
    private final Set<String> streamsCountryCode;
    private final Set<String> cityCode;
    private final Set<String> cityName;
    private final List<String> companyKeys;
    private final List<String> profitCenterKeys;
    private final List<String> parentRepOwnerKeys;

    private final String currency;

    private final Set<String> campaignIds;
    private final Set<String> adSetIds;
    private final Set<Integer> decibelLabelIds;
    private final Set<String> adsAccounts;
    private final Set<Integer> providerIds;
    private final Set<String> referrerDomains;
    private final Set<String> platforms;
    private final Set<Integer> subCategoryIds;
    private final Integer userId;
    private final Set<GroupByAdsPerformance> adsPerformancesGroupBy;
    private final Set<AdsDspSlug> adsDsps;
    private final Boolean isAssigned;
    private final Boolean isPending;

    private final String cursor;
    private final List<String> dsp;
    private final VideoDspSlug videoDsp;
    private final String dspId;
    private final LocalDate endDate;
    private final String groupBy;
    private final TracksGroupByField tracksGroupByField;
    private final GroupByField groupByField;
    private final Set<GroupByField> groupByFields;
    private final Set<GroupByVideo> groupByFieldsVideo;
    private final Set<YouTubeMetricsCategory> metricCategories;
    private final List<String> include;
    private final Set<IncludeVideoChart> includeVideChart;
    private final Set<IncludeChart> includeChart;
    private final Set<ExpandTo> expandTo;
    private final Set<IncludeStreams> includeStreams;
    private final Boolean isSonyVideo;
    private final Boolean isSony;
    private final Boolean isExplicit;
    private final Boolean includeIgnored;
    private final Boolean includeRemoved;
    private final IndexParam index;
    private final List<String> isrc;
    private final Set<TikTokContentType> tikTokContentTypes;
    private final Set<TikTokBreakdown> tikTokBreakdowns;
    private final Set<TikTokMetric> metrics;
    private final Set<TikTokChartType> tikTokChartTypes;
    private final TikTokTopIsrcVideoTopBy tikTokTopIsrcVideoTopBy;
    private final Set<Integer> periodDays;
    private final Set<DataHealthBreakdownGroup> dataHealthBreakdownGroups;
    private final Set<DataHealthBreakdown> dataHealthBreakdowns;
    private final Set<DataHealthStatus> dataHealthStatus;
    private final String labelId;
    private final Integer limit;
    private final String limitRange;
    private final Integer offset;
    private final Integer page;
    private final List<String> playlistId;
    private final Set<String> playlistTypes;
    private final Set<String> playlistOwnerCategoryId;
    private final String playlistNameContains;
    private final String productId;
    private final List<String> productIds;
    private final String query;
    private final String regionId;
    private final String sortBy;
    private final SortOrder sortOrder;
    private final List<SortWithOrder> sortList;
    private final LocalDate startDate;
    private final SubsetParam subset;
    private final List<String> trackId;
    private final List<String> upc;
    private final List<String> brand;
    private final Set<IncludeTracks> includeTracks;
    private final Set<TrackMediaType> mediaTypeKeys;
    private final Boolean isProductFamilyHead;
    private final Integer productVersionNo;
    private final Integer productFamilyNo;
    private final Set<String> productFamilyIds;
    private final String configCatKey;
    private final String configKey;
    private final String projectId;
    private final Set<String> projectIds;
    private final Set<String> projectNumbers;
    private final List<String> repOwnerKey;
    private final Boolean isExit;
    private final boolean isNewListeners;
    private final Integer minPosition;
    private final Integer maxPosition;
    private final boolean considerConfidential;
    private final Double percentChange;

    //spotify and apple charts
    private final Set<AnalyticsMetrics> analyticsMetrics;
    private final Set<TrackStateIncludes> trackStateIncludes;
    private final Set<ChartTrackStateIncludes> chartTrackStateIncludes;

    private final Set<SpotifyChartType> types;
    private final SpotifyChartType chartType;
    private final Set<SpotifyChartBreakdown> breakdowns;
    private final SpotifyChartBreakdown chartBreakdown;
    private final Integer minRank;
    private final Integer maxRank;
    private final String singleChartId;
    private final LocalDate date;
    private final ChartTrackMetricsDimension chartTrackMetricsDimension;
    private final LocalDate previousDate;
    private final Set<TikTokStateMetric> tikTokTrackStateIncludes;
    private final Set<TikTokTopAnalyticMetric> tikTokTopAnalyticMetrics;
    private final Set<JunoSpotifyStreamsIncludes> junoSpotifyStreamsIncludes;
    private final Set<String> genreName;
    private final LocalDate minAddedDate;
    private final LocalDate minRemovedDate;

    public Set<String> getIsrcSet() {
        if (isrc == null) {
            return null;
        }

        return CollectionUtils.toSet(isrc);
    }

    public Set<String> getTrackIdSet() {
        if (trackId == null) {
            return null;
        }

        return CollectionUtils.toSet(trackId);
    }

    public boolean hasAnyVideoParams() {
        return CollectionUtils.isNotEmpty(videoIds)
            || CollectionUtils.isNotEmpty(artistId)
            || CollectionUtils.isNotEmpty(channelId)
            || CollectionUtils.isNotEmpty(isrc);
    }

    public Params copyVideoParams() {
        return Params.builder()
            .isrc(getIsrc())
            .expandTo(getExpandTo())
            .youTubeContentTypes(getYouTubeContentTypes())
            .minPremiumUgcViews(getMinPremiumUgcViews())
            .artistId(getArtistId())
            .videoDsp(getVideoDsp())
            .videoIds(getVideoIds())
            .build();
    }

    /**
     * if cursorDate + (limitRangeDays - 1) >= endDate, return null
     */
    public String getNextCursor() {
        if (hasLimitRange()) {
            LocalDate nextCursorDate = getEndDate().plusDays(1);
            if (!nextCursorDate.isAfter(endDate)) {
                return ApplicationConstants.PAGE_CURSOR_PREFIX_DATE + nextCursorDate;
            }
        }
        return null;
    }

    public LocalDate getStartDate() {
        if (hasLimitRange() && hasCursor()) {
            if (getCursorDate().isAfter(startDate) && !getCursorDate().isAfter(endDate)) {
                return getCursorDate();
            }
        }
        return startDate;
    }

    /**
     * @return the last date to include, inclusive
     */
    public LocalDate getEndDate() {
        if (hasLimitRange()) {
            LocalDate newEndDate = getStartDate().plusDays(getLimitRangeDays() - 1);
            if (hasCursor()) {
                LocalDate cursorEndDate = getCursorDate();
                if (cursorEndDate.isBefore(endDate) && !cursorEndDate.isBefore(startDate)) {
                    newEndDate = cursorEndDate.plusDays(getLimitRangeDays() - 1);
                }
            }
            if (newEndDate.isBefore(endDate) && !newEndDate.isBefore(startDate)) {
                return newEndDate;
            }
        }
        return endDate;
    }

    private boolean hasLimitRange() {
        return CollectionUtils.isNotEmpty(limitRange);
    }

    private boolean hasCursor() {
        return CollectionUtils.isNotEmpty(cursor);
    }

    private int getLimitRangeDays() {
        return Integer.parseInt(limitRange.replace(ApplicationConstants.PAGE_LIMIT_RANGE_PREFIX_DAYS, ""));
    }

    private LocalDate getCursorDate() {
        return LocalDate.parse(cursor.replace(ApplicationConstants.PAGE_CURSOR_PREFIX_DATE, ""));
    }

    public boolean hasOffset() {
        return getOffset() != null && getOffset() > 0;
    }

    public boolean hasLimit() {
        return getLimit() != null && getLimit() > 0;
    }

    public String getDataHealthStatusesAsString() {
        return CollectionUtils.isEmpty(dataHealthStatus) ? null
            : dataHealthStatus.stream().map(DataHealthStatus::getDbValue).collect(Collectors.joining(","));
    }

}
