package io.delphiplatform.api.v3.model.trackposition;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;

import javax.validation.Valid;
import javax.validation.constraints.Pattern;

import io.delphiplatform.api.util.model.ModelUtils;
import io.delphiplatform.api.v3.constant.ApplicationConstants;
import io.delphiplatform.api.v3.constant.DspConstants;
import io.delphiplatform.api.v3.model.PlaylistDates;
import io.delphiplatform.api.v3.model.PlaylistDatesAwareModel;
import io.delphiplatform.api.v3.model.PlaylistStreamingForPeriodsInfo;
import io.delphiplatform.api.v3.model.PublicPlaylist;
import io.delphiplatform.api.v3.model.PublicPlaylistableModel;
import io.delphiplatform.api.v3.model.StreamingInfoAwareModel;
import io.delphiplatform.api.v3.model.TrackAwareModel;
import io.delphiplatform.api.v3.model.TrackPosition;
import io.delphiplatform.api.v3.model.TrackSimple;
import io.delphiplatform.api.v3.model.amazon.AmazonTrackShortInfo;
import io.delphiplatform.api.v3.model.apple.AppleTrack;
import io.delphiplatform.api.v3.model.spotify.SpotifyTrack;

/**
 * PublicPlaylistTrackPosition
 */
@JsonPropertyOrder(alphabetic = true)
public class PublicPlaylistTrackPosition extends TrackPosition implements PublicPlaylistableModel, TrackAwareModel,
    PlaylistDatesAwareModel, StreamingInfoAwareModel {

    @JsonProperty("dsp_track_id")
    private String dspTrackId;

    //if serialization is introduced (for Spotify DSP) - return field through transformation (see DAPD-2428)
    @JsonIgnore
    private String countryCode;

    @JsonProperty("playlist_id")
    private String playlistId;

    @JsonProperty("playlist")
    private PublicPlaylist playlist;

    @JsonProperty("playlist_dates")
    private PlaylistDates playlistDates;

    //'technical' playlist object that wont be serialized/returned to client etc.
    @JsonIgnore
    private PublicPlaylist transientPlaylist;

    @JsonProperty("streaming_info")
    private Map<String, PlaylistStreamingForPeriodsInfo> streamingInfo;

    @JsonProperty("trend")
    private Long trend;

    @JsonProperty("num_days_on")
    private Long daysInPlaylist;

    /* As PublicPlaylistTrackPosition used as intermediate object for "streams_for_period" calculation,
     * this field added as transitive for next step data object */
    @JsonIgnore
    private LocalDateTime previousPositionDateTime;

    @JsonProperty("earliest_position_date_time")
    @JsonFormat(shape = Shape.STRING, pattern = ApplicationConstants.ISO_DATE_TIME_PATTERN)
    private LocalDateTime earliestPositionDateTime;

    @JsonProperty("latest_position_date_time")
    @JsonFormat(shape = Shape.STRING, pattern = ApplicationConstants.ISO_DATE_TIME_PATTERN)
    private LocalDateTime latestPositionDateTime;

    @JsonProperty("previous_position_change_14_days")
    private Long previousPositionChange14Days;

    @JsonProperty("trend_change_14_days")
    private Long trendChange14Days;

    @JsonProperty("last_date_change_14_days")
    private LocalDate lastDateChange14Days;

    @JsonProperty("is_top_track_for_isrc")
    private Boolean topTrackForIsrc;

    @JsonProperty("earliest_position")
    private Long earliestPosition;

    @JsonProperty("last_added_date_time")
    @JsonFormat(shape = Shape.STRING, pattern = ApplicationConstants.ISO_DATE_TIME_PATTERN)
    private LocalDateTime lastAddedDateTime;

    @JsonProperty("track")
    private TrackSimple track;

    @JsonProperty("apple_track")
    private AppleTrack appleTrack;

    @JsonProperty("spotify_track")
    private SpotifyTrack spotifyTrack;

    @JsonProperty("amazon_track")
    private AmazonTrackShortInfo amazonTrack;

    @JsonProperty("num_tracks")
    protected Long numTracks;

    public PublicPlaylistTrackPosition trackId(String trackId) {
        this.dspTrackId = trackId;
        return this;
    }

    /**
     * Get track id
     *
     * @return trackId
     */
    public String getDspTrackId() {
        return dspTrackId;
    }

    public void setDspTrackId(String dspTrackId) {
        this.dspTrackId = dspTrackId;
    }

    public PublicPlaylistTrackPosition countryCode(String countryCode) {
        this.countryCode = countryCode;
        return this;
    }

    /**
     * Get country code
     *
     * @return countryCode
     */
    public String getCountryCode() {
        return countryCode;
    }

    public void setCountryCode(String countryCode) {
        this.countryCode = countryCode;
    }

    public PublicPlaylistTrackPosition isrc(String isrc) {
        this.isrc = isrc;
        return this;
    }

    /**
     * The International Standard Recording Code (ISRC) for a song.
     *
     * @return isrc
     */
    @Pattern(regexp = ApplicationConstants.ISRC_PATTERN)
    public String getIsrc() {
        return isrc;
    }

    public void setIsrc(String isrc) {
        this.isrc = isrc;
    }

    public PublicPlaylistTrackPosition dsp(String dsp) {
        this.dsp = dsp;
        return this;
    }

    /**
     * Slug name of a DSP
     *
     * @return dsp
     */
    @Pattern(regexp = "^[a-z]*$")
    public String getDsp() {
        return dsp;
    }

    public void setDsp(String dsp) {
        this.dsp = dsp;
    }

    public PublicPlaylistTrackPosition current(Long current) {
        this.current = current;
        return this;
    }

    /**
     * Get current
     *
     * @return current
     */
    public Long getCurrent() {
        return current;
    }

    public void setCurrent(Long current) {
        this.current = current;
    }

    public PublicPlaylistTrackPosition previous(Long previous) {
        this.previous = previous;
        return this;
    }

    /**
     * Get previous
     *
     * @return previous
     */
    public Long getPrevious() {
        return previous;
    }

    public void setPrevious(Long previous) {
        this.previous = previous;
    }

    public PublicPlaylistTrackPosition top(Integer top) {
        this.top = top;
        return this;
    }

    /**
     * Get top
     *
     * @return top
     */
    public Integer getTop() {
        return top;
    }

    public void setTop(Integer top) {
        this.top = top;
    }

    public PublicPlaylistTrackPosition numWeeksOn(Integer numWeeksOn) {
        this.numWeeksOn = numWeeksOn;
        return this;
    }

    /**
     * Get numWeeksOn
     *
     * @return numWeeksOn
     */
    public Integer getNumWeeksOn() {
        return numWeeksOn;
    }

    public void setNumWeeksOn(Integer numWeeksOn) {
        this.numWeeksOn = numWeeksOn;
    }

    /**
     * Get numTracks
     *
     * @return numTracks
     */
    public Long getNumTracks() {
        return numTracks;
    }

    public void setNumTracks(Long numTracks) {
        this.numTracks = numTracks;
    }

    public PublicPlaylistTrackPosition date(LocalDate date) {
        this.date = date;
        return this;
    }

    /**
     * ISO 8601 date in format `YYYY-MM-DD`
     *
     * @return date
     */
    @Valid
    @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$")
    public LocalDate getDate() {
        return date;
    }

    public void setDate(LocalDate date) {
        this.date = date;
    }

    public PublicPlaylistTrackPosition playlistId(String playlistId) {
        this.playlistId = playlistId;
        return this;
    }

    /**
     * Primary key identifier for Playlist
     *
     * @return playlistId
     */
    public String getPlaylistId() {
        return playlistId;
    }

    public void setPlaylistId(String playlistId) {
        this.playlistId = playlistId;
    }

    public PublicPlaylistTrackPosition playlist(PublicPlaylist playlist) {
        this.playlist = playlist;
        return this;
    }

    /**
     * Get playlist
     *
     * @return playlist
     */
    @Valid
    public PublicPlaylist getPlaylist() {
        return playlist;
    }

    public void setPlaylist(PublicPlaylist playlist) {
        this.playlist = playlist;
    }

    public PublicPlaylistTrackPosition transientPlaylist(PublicPlaylist transientPlaylist) {
        this.transientPlaylist = transientPlaylist;
        return this;
    }

    /**
     * Get playlistDates
     *
     * @return playlistDates
     */
    public PlaylistDates getPlaylistDates() {
        return playlistDates;
    }

    public void setPlaylistDates(PlaylistDates playlistDates) {
        this.playlistDates = playlistDates;
    }

    /**
     * Get 'technical' playlist object that wont be serialized/returned to client etc.
     *
     * @return transientPlaylist
     */
    @Valid
    public PublicPlaylist getTransientPlaylist() {
        return transientPlaylist;
    }

    public void setTransientPlaylist(PublicPlaylist transientPlaylist) {
        this.transientPlaylist = transientPlaylist;
    }

    public PublicPlaylistTrackPosition streamingInfo(Map<String, PlaylistStreamingForPeriodsInfo> streamingInfo) {
        this.streamingInfo = streamingInfo;
        return this;
    }

    /**
     * Get streaming info
     *
     * @return streamingInfo
     */
    public Map<String, PlaylistStreamingForPeriodsInfo> getStreamingInfo() {
        return streamingInfo;
    }

    public void setStreamingInfo(Map<String, PlaylistStreamingForPeriodsInfo> streamingInfo) {
        this.streamingInfo = streamingInfo;
    }

    public PublicPlaylistTrackPosition trend(Long trend) {
        this.trend = trend;
        return this;
    }

    /**
     * Get trend
     *
     * @return trend
     */
    public Long getTrend() {
        return trend;
    }

    public void setTrend(Long trend) {
        this.trend = trend;
    }

    public PublicPlaylistTrackPosition daysInPlaylist(Long daysInPlaylist) {
        this.daysInPlaylist = daysInPlaylist;
        return this;
    }

    /**
     * Get number of track days in playlist
     *
     * @return daysInPlaylist
     */
    public Long getDaysInPlaylist() {
        return daysInPlaylist;
    }

    public void setDaysInPlaylist(Long daysInPlaylist) {
        this.daysInPlaylist = daysInPlaylist;
    }

    public PublicPlaylistTrackPosition previousPositionDateTime(LocalDateTime previousPositionDateTime) {
        this.previousPositionDateTime = previousPositionDateTime;
        return this;
    }

    /**
     * Get previous position date
     *
     * @return previousPositionDateTime
     */
    public LocalDateTime getPreviousPositionDateTime() {
        return previousPositionDateTime;
    }

    public void setPreviousPositionDateTime(LocalDateTime previousPositionDateTime) {
        this.previousPositionDateTime = previousPositionDateTime;
    }

    public PublicPlaylistTrackPosition earliestPositionDateTime(LocalDateTime earliestPositionDateTime) {
        this.earliestPositionDateTime = earliestPositionDateTime;
        return this;
    }

    /**
     * Get earliest position date
     *
     * @return earliestPositionDate
     */
    public LocalDateTime getEarliestPositionDateTime() {
        return earliestPositionDateTime;
    }

    public void setEarliestPositionDateTime(LocalDateTime earliestPositionDateTime) {
        this.earliestPositionDateTime = earliestPositionDateTime;
    }

    public PublicPlaylistTrackPosition latestPositionDate(LocalDateTime latestPositionDateTime) {
        this.latestPositionDateTime = latestPositionDateTime;
        return this;
    }

    /**
     * Get latest position date
     *
     * @return latestPositionDate
     */
    public LocalDateTime getLatestPositionDateTime() {
        return latestPositionDateTime;
    }

    public void setLatestPositionDateTime(LocalDateTime latestPositionDateTime) {
        this.latestPositionDateTime = latestPositionDateTime;
    }

    public PublicPlaylistTrackPosition previousPositionChange14Days(Long previousPositionChange14Days) {
        this.previousPositionChange14Days = previousPositionChange14Days;
        return this;
    }

    /**
     * Get previous position change in 14 days
     *
     * @return previousPositionChange14Days
     */
    public Long getPreviousPositionChange14Days() {
        return previousPositionChange14Days;
    }

    public void setPreviousPositionChange14Days(Long previousPositionChange14Days) {
        this.previousPositionChange14Days = previousPositionChange14Days;
    }

    public PublicPlaylistTrackPosition trendChange14Days(Long trendChange14Days) {
        this.trendChange14Days = trendChange14Days;
        return this;
    }

    /**
     * Get trend change in 14 days
     *
     * @return trendChange14Days
     */
    public Long getTrendChange14Days() {
        return trendChange14Days;
    }

    public void setTrendChange14Days(Long trendChange14Days) {
        this.trendChange14Days = trendChange14Days;
    }

    public PublicPlaylistTrackPosition lastDateChange14Days(LocalDate lastDateChange14Days) {
        this.lastDateChange14Days = lastDateChange14Days;
        return this;
    }

    /**
     * Get last date change in 14 days
     *
     * @return lastDateChange14Days
     */
    public LocalDate getLastDateChange14Days() {
        return lastDateChange14Days;
    }

    public void setLastDateChange14Days(LocalDate lastDateChange14Days) {
        this.lastDateChange14Days = lastDateChange14Days;
    }

    public PublicPlaylistTrackPosition topTrackForIsrc(Boolean topTrackForIsrc) {
        this.topTrackForIsrc = topTrackForIsrc;
        return this;
    }

    /**
     * Get is top track for isrc flag
     *
     * @return topTrackForIsrc
     */
    public Boolean getTopTrackForIsrc() {
        return topTrackForIsrc;
    }

    public void setTopTrackForIsrc(Boolean topTrackForIsrc) {
        this.topTrackForIsrc = topTrackForIsrc;
    }

    public PublicPlaylistTrackPosition track(TrackSimple track) {
        this.track = track;
        return this;
    }

    public PublicPlaylistTrackPosition earliestPosition(Long earliestPosition) {
        this.earliestPosition = earliestPosition;
        return this;
    }

    /**
     * Get earliestPosition
     *
     * @return earliestPosition
     */
    public Long getEarliestPosition() {
        return earliestPosition;
    }

    public void setEarliestPosition(Long earliestPosition) {
        this.earliestPosition = earliestPosition;
    }

    public PublicPlaylistTrackPosition lastAddedDateTime(LocalDateTime lastAddedDateTime) {
        this.lastAddedDateTime = lastAddedDateTime;
        return this;
    }

    /**
     * Get lastAddedDateTime
     *
     * @return lastAddedDateTime
     */
    public LocalDateTime getLastAddedDateTime() {
        return lastAddedDateTime;
    }

    public void setLastAddedDateTime(LocalDateTime lastAddedDateTime) {
        this.lastAddedDateTime = lastAddedDateTime;
    }

    /**
     * Get GRAS track metadata
     *
     * @return track
     */
    public TrackSimple getTrack() {
        return track;
    }

    public void setTrack(TrackSimple track) {
        this.track = track;
    }

    public PublicPlaylistTrackPosition appleTrack(AppleTrack appleTrack) {
        this.appleTrack = appleTrack;
        return this;
    }

    /**
     * Get apple track metadata
     *
     * @return appleTrack
     */
    public AppleTrack getAppleTrack() {
        return appleTrack;
    }

    public void setAppleTrack(AppleTrack appleTrack) {
        this.appleTrack = appleTrack;
    }

    public PublicPlaylistTrackPosition spotifyTrack(SpotifyTrack spotifyTrack) {
        this.spotifyTrack = spotifyTrack;
        return this;
    }

    /**
     * Get spotify track metadata
     *
     * @return spotifyTrack
     */
    public SpotifyTrack getSpotifyTrack() {
        return spotifyTrack;
    }

    public void setSpotifyTrack(SpotifyTrack spotifyTrack) {
        this.spotifyTrack = spotifyTrack;
    }

    public PublicPlaylistTrackPosition amazonTrack(AmazonTrackShortInfo amazonTrack) {
        this.amazonTrack = amazonTrack;
        return this;
    }

    /**
     * Get amazon track metadata
     *
     * @return amazonTrack
     */
    public AmazonTrackShortInfo getAmazonTrack() {
        return amazonTrack;
    }

    public void setAmazonTrack(AmazonTrackShortInfo amazonTrack) {
        this.amazonTrack = amazonTrack;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        PublicPlaylistTrackPosition playlistTrackPosition = (PublicPlaylistTrackPosition) o;
        return Objects.equals(this.dspTrackId, playlistTrackPosition.dspTrackId) &&
            Objects.equals(this.countryCode, playlistTrackPosition.countryCode) &&
            Objects.equals(this.isrc, playlistTrackPosition.isrc) &&
            Objects.equals(this.dsp, playlistTrackPosition.dsp) &&
            Objects.equals(this.current, playlistTrackPosition.current) &&
            Objects.equals(this.previous, playlistTrackPosition.previous) &&
            Objects.equals(this.numTracks, playlistTrackPosition.numTracks) &&
            Objects.equals(this.top, playlistTrackPosition.top) &&
            Objects.equals(this.numWeeksOn, playlistTrackPosition.numWeeksOn) &&
            Objects.equals(this.date, playlistTrackPosition.date) &&
            Objects.equals(this.playlistId, playlistTrackPosition.playlistId) &&
            Objects.equals(this.playlist, playlistTrackPosition.playlist) &&
            Objects.equals(this.playlistDates, playlistTrackPosition.playlistDates) &&
            Objects.equals(this.trend, playlistTrackPosition.trend) &&
            Objects.equals(this.daysInPlaylist, playlistTrackPosition.daysInPlaylist) &&
            Objects.equals(this.previousPositionDateTime, playlistTrackPosition.previousPositionDateTime) &&
            Objects.equals(this.earliestPositionDateTime, playlistTrackPosition.earliestPositionDateTime) &&
            Objects.equals(this.latestPositionDateTime, playlistTrackPosition.latestPositionDateTime) &&
            Objects.equals(this.streamingInfo, playlistTrackPosition.streamingInfo) &&
            Objects.equals(this.previousPositionChange14Days, playlistTrackPosition.previousPositionChange14Days) &&
            Objects.equals(this.trendChange14Days, playlistTrackPosition.trendChange14Days) &&
            Objects.equals(this.lastDateChange14Days, playlistTrackPosition.lastDateChange14Days) &&
            Objects.equals(this.topTrackForIsrc, playlistTrackPosition.topTrackForIsrc) &&
            Objects.equals(this.track, playlistTrackPosition.track) &&
            Objects.equals(this.amazonTrack, playlistTrackPosition.amazonTrack) &&
            Objects.equals(this.appleTrack, playlistTrackPosition.appleTrack) &&
            Objects.equals(this.spotifyTrack, playlistTrackPosition.spotifyTrack) &&
            Objects.equals(this.earliestPosition, playlistTrackPosition.earliestPosition) &&
            Objects.equals(this.lastAddedDateTime, playlistTrackPosition.lastAddedDateTime);
    }

    @Override
    public int hashCode() {
        return Objects.hash(dspTrackId, countryCode, isrc, dsp, current, previous, top, numWeeksOn, date, playlistId,
            playlist, numTracks,
            trend, daysInPlaylist, previousPositionDateTime, earliestPositionDateTime, latestPositionDateTime, streamingInfo,
            previousPositionChange14Days, trendChange14Days, lastDateChange14Days, topTrackForIsrc, track, amazonTrack,
            appleTrack, spotifyTrack, earliestPosition, lastAddedDateTime, playlistDates);
    }

    @Override
    public String toString() {

        String sb = "class PlaylistTrackPosition {\n"
            + "    dspTrackId: " + toIndentedString(dspTrackId) + "\n"
            + "    countryCode: " + toIndentedString(countryCode) + "\n"
            + "    isrc: " + toIndentedString(isrc) + "\n"
            + "    dsp: " + toIndentedString(dsp) + "\n"
            + "    current: " + toIndentedString(current) + "\n"
            + "    previous: " + toIndentedString(previous) + "\n"
            + "    numTracks: " + toIndentedString(numTracks) + "\n"
            + "    top: " + toIndentedString(top) + "\n"
            + "    numWeeksOn: " + toIndentedString(numWeeksOn) + "\n"
            + "    date: " + toIndentedString(date) + "\n"
            + "    playlistId: " + toIndentedString(playlistId) + "\n"
            + "    playlist: " + toIndentedString(playlist) + "\n"
            + "    playlistDates: " + toIndentedString(playlistDates) + "\n"
            + "    trend: " + toIndentedString(trend) + "\n"
            + "    daysInPlaylist: " + toIndentedString(daysInPlaylist) + "\n"
            + "    previousPositionDateTime: " + toIndentedString(previousPositionDateTime) + "\n"
            + "    earliestPositionDateTime: " + toIndentedString(earliestPositionDateTime) + "\n"
            + "    latestPositionDateTime: " + toIndentedString(latestPositionDateTime) + "\n"
            + "    streamingInfo: " + toIndentedString(streamingInfo) + "\n"
            + "    previousPositionChange14Days: " + toIndentedString(previousPositionChange14Days) + "\n"
            + "    trendChange14Days: " + toIndentedString(trendChange14Days) + "\n"
            + "    lastDateChange14Days: " + toIndentedString(lastDateChange14Days) + "\n"
            + "    topTrackForIsrc: " + toIndentedString(topTrackForIsrc) + "\n"
            + "    track: " + toIndentedString(track) + "\n"
            + "    amazonTrack: " + toIndentedString(amazonTrack) + "\n"
            + "    appleTrack: " + toIndentedString(appleTrack) + "\n"
            + "    spotifyTrack: " + toIndentedString(spotifyTrack) + "\n"
            + "    earliestPosition: " + toIndentedString(earliestPosition) + "\n"
            + "    lastAddedDateTime: " + toIndentedString(lastAddedDateTime) + "\n"
            + "}";
        return sb;
    }

    @JsonIgnore
    public String getAmazonTrackIdUnPrefixed() {
        if (!DspConstants.AMAZON.equals(getDsp())) {
            throw new IllegalStateException("Only Amazon DSP is supported");
        }

        return ModelUtils.getAmazonTrackIdUnPrefixed(getDspTrackId());
    }

    @JsonIgnore
    public String getBasePlaylistIdFromAmazonFullPlaylistId() {
        if (!DspConstants.AMAZON.equals(getDsp())) {
            throw new IllegalStateException("Only Amazon DSP is supported");
        }

        return ModelUtils.getBasePlaylistIdFromAmazonFullPlaylistId(getPlaylistId());
    }

    /**
     * Convert the given object to string with each line indented by 4 spaces (except the first line).
     */
    private String toIndentedString(Object o) {
        if (o == null) {
            return "null";
        }
        return o.toString().replace("\n", "\n    ");
    }
}
