package io.delphiplatform.api.v3.model;

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

import org.openapitools.jackson.nullable.JsonNullable;

import java.net.URI;
import java.util.Objects;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.util.model.ModelUtils;
import io.delphiplatform.api.v3.constant.DspConstants;
import io.delphiplatform.api.v3.model.service.ImageService;
import io.delphiplatform.api.v3.rdb.entity.PlaylistEntity;
import io.delphiplatform.api.v3.rdb.entity.PlaylistProjection;
import io.delphiplatform.api.v3.rdb.entity.apple.music.PlaylistPublicAppleMusicEntity;
import io.delphiplatform.api.v3.rdb.entity.spotify.PlaylistPublicSpotifyEntity;

/**
 * Playlist public
 */
@JsonPropertyOrder(alphabetic = true)
public class PublicPlaylist {

    @JsonProperty("name")
    private String name;

    @JsonProperty("playlist_id")
    private String playlistId;

    @JsonProperty("playlist_type")
    private String playlistType;

    @JsonProperty("uri")
    private JsonNullable<URI> uri = JsonNullable.undefined();

    @JsonProperty("num_tracks")
    private JsonNullable<Integer> numTracks = JsonNullable.undefined();

    @JsonIgnore
    private JsonNullable<String> countryCode = JsonNullable.undefined();

    @JsonProperty("rank")
    private JsonNullable<Integer> rank = JsonNullable.undefined();

    @JsonProperty("dsp")
    private Dsp dsp;

    @JsonProperty("dsp_playlist_id")
    private String dspPlaylistId;

    @JsonProperty("amazon_playlist_id")
    private String amazonPlaylistId;

    @JsonProperty("description")
    private String description;

    @JsonProperty("followers")
    private Integer followers;

    @JsonProperty("is_ignored")
    private Boolean ignored;

    @JsonProperty("is_removed")
    private Boolean removed;

    @JsonProperty("is_personalised")
    private Boolean personalised;

    @JsonProperty("is_public")
    private Boolean publicPlaylist;

    @JsonProperty("owner")
    private PlaylistOwner owner;

    @JsonProperty("image")
    private JsonNullable<ImageSimple> image = JsonNullable.undefined();

    @JsonIgnore
    private boolean saveHistory = true;

    @JsonIgnore
    private boolean saveTrackList = true;

    public static PublicPlaylist from(PlaylistEntity playlist, ImageService imageService) {
        if (playlist == null) {
            return null;
        }
        return new PublicPlaylist()
            .countryCode(playlist.getCountryCode())
            .dsp(Dsp.from(playlist.getDsp()))
            .dspPlaylistId(playlist.getDspPlaylistId())
            .name(playlist.getName())
            .numTracks(playlist.getNumTracks())
            .playlistId(playlist.getPlaylistId())
            .playlistType(CollectionUtils.isEmpty(playlist.getPlaylistType()) ? null : playlist.getPlaylistType())
            .rank(playlist.getRank())
            .image(imageService.resolveImage(playlist))
            .uri(ModelUtils.createUri(playlist.getUri()))
            .description(playlist.getDescription())
            .followers(playlist.getFollowers())
            .ignored(playlist.getIgnored())
            .removed(playlist.getRemoved())
            .personalised(playlist.getPersonalised())
            .publicPlaylist(playlist.getPublicPlaylist());
    }

    public static PublicPlaylist from(PlaylistPublicAppleMusicEntity playlist, ImageService imageService) {
        if (playlist == null) {
            return null;
        }
        return new PublicPlaylist()
            .countryCode(playlist.getCountryCode())
            .dsp(Dsp.from(playlist.getDsp()))
            .dspPlaylistId(playlist.getDspPlaylistId())
            .name(playlist.getName())
            .numTracks(playlist.getNumTracks())
            .playlistId(playlist.getPlaylistId())
            .playlistType(CollectionUtils.isEmpty(playlist.getPlaylistType()) ? null : playlist.getPlaylistType())
            .rank(playlist.getRank())
            .image(imageService.resolveImage(playlist))
            .uri(ModelUtils.createUri(playlist.getUri()))
            .description(playlist.getDescription())
            .followers(playlist.getFollowers())
            .ignored(playlist.getIgnored())
            .removed(playlist.getRemoved())
            .personalised(playlist.getPersonalised())
            .publicPlaylist(playlist.getPublicPlaylist())
            .owner(PlaylistOwner.from(playlist.getOwner()))
            .saveHistory(Boolean.TRUE.equals(playlist.getSaveHistory()) || playlist.getSaveHistory() == null)
            .saveTrackList(Boolean.TRUE.equals(playlist.getSaveTrackList()) || playlist.getSaveTrackList() == null);
    }

    public static PublicPlaylist from(PlaylistPublicSpotifyEntity playlist, ImageService imageService) {
        if (playlist == null) {
            return null;
        }
        return new PublicPlaylist()
            .countryCode(playlist.getCountryCode())
            .dsp(Dsp.from(playlist.getDsp()))
            .dspPlaylistId(playlist.getDspPlaylistId())
            .name(playlist.getName())
            .numTracks(playlist.getNumTracks())
            .playlistId(playlist.getPlaylistId())
            .playlistType(CollectionUtils.isEmpty(playlist.getPlaylistType()) ? null : playlist.getPlaylistType())
            .rank(playlist.getRank())
            .image(imageService.resolveImage(playlist))
            .uri(ModelUtils.createUri(playlist.getUri()))
            .description(playlist.getDescription())
            .followers(playlist.getFollowers())
            .ignored(playlist.getIgnored())
            .removed(playlist.getRemoved())
            .personalised(playlist.getPersonalised())
            .publicPlaylist(playlist.getPublicPlaylist())
            .owner(PlaylistOwner.from(playlist.getOwner()))
            .saveHistory(Boolean.TRUE.equals(playlist.getSaveHistory()) || playlist.getSaveHistory() == null)
            .saveTrackList(Boolean.TRUE.equals(playlist.getSaveTrackList()) || playlist.getSaveTrackList() == null);
    }

    public static PublicPlaylist from(PlaylistProjection playlist, ImageService imageService) {
        if (playlist == null) {
            return null;
        }
        return new PublicPlaylist()
            .countryCode(playlist.getCountryCodeValue())
            .dsp(new Dsp()
                .dspId(playlist.getDspId())
                .name(playlist.getDspName())
                .slug(playlist.getDspSlug()))
            .dspPlaylistId(playlist.getDspPlaylistId())
            .name(playlist.getName())
            .numTracks(playlist.getNumTracks())
            .playlistId(playlist.getPlaylistId())
            .playlistType(CollectionUtils.isEmpty(playlist.getPlaylistType()) ? null : playlist.getPlaylistType())
            .rank(playlist.getRank())
            .image(imageService.resolveImage(
                playlist.getDspId(),
                playlist.getDspPlaylistId(),
                ModelUtils.isDspWithMultipleCountriesPlaylistsUnderSamePlaylistId(playlist.getDspId()) ? playlist.getCountryCodeValue() : null,
                playlist.getArtworkUrl()
            ))
            .uri(ModelUtils.createUri(playlist.getUri()))
            .description(playlist.getDescription())
            .followers(playlist.getFollowers())
            .ignored(playlist.getIsIgnored())
            .removed(playlist.getIsRemoved())
            .personalised(playlist.getIsPersonalised())
            .publicPlaylist(playlist.getIsPublic())
            .owner(PlaylistOwner.from(playlist));
    }

    public PublicPlaylist name(String name) {
        this.name = name;
        return this;
    }

    /**
     * Get name
     *
     * @return name
     */
    @NotNull
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

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

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

    public PublicPlaylist uri(URI uri) {
        this.uri = JsonNullable.of(uri);
        return this;
    }

    public PublicPlaylist playlistType(String playlistType) {
        this.playlistType = playlistType;
        return this;
    }

    @NotNull
    public String getPlaylistType() {
        return playlistType;
    }

    public void setPlaylistType(String playlistType) {
        this.playlistType = playlistType;
    }

    /**
     * DSP URI to the playlist
     *
     * @return uri
     */
    @Valid
    public JsonNullable<URI> getUri() {
        return uri;
    }

    public void setUri(JsonNullable<URI> uri) {
        this.uri = uri;
    }

    public PublicPlaylist numTracks(Integer numTracks) {
        this.numTracks = JsonNullable.of(numTracks);
        return this;
    }

    /**
     * Total number of tracks in the playlist
     *
     * @return numTracks
     */
    public JsonNullable<Integer> getNumTracks() {
        return numTracks;
    }

    public void setNumTracks(JsonNullable<Integer> numTracks) {
        this.numTracks = numTracks;
    }

    public PublicPlaylist countryCode(String countryCode) {
        this.countryCode = JsonNullable.of(countryCode);
        return this;
    }

    /**
     * Typically a lower-case two letter code for the country
     *
     * @return countryCode
     */
    public JsonNullable<String> getCountryCode() {
        return countryCode;
    }

    @JsonProperty("country_code")
    public JsonNullable<String> getCountryCodeForRepresentation() {
        if (this.dsp != null && DspConstants.SPOTIFY.equals(this.dsp.getDspId())) {
            return JsonNullable.of(
                ModelUtils.publicSpotifyCountryCodeDataToRepresentationState(this.countryCode.orElse(null))
            );
        }

        return countryCode;
    }

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

    public PublicPlaylist rank(Integer rank) {
        this.rank = JsonNullable.of(rank);
        return this;
    }

    /**
     * SME internal ranking latest for global region
     *
     * @return rank
     */
    public JsonNullable<Integer> getRank() {
        return rank;
    }

    public void setRank(JsonNullable<Integer> rank) {
        this.rank = rank;
    }

    public PublicPlaylist dsp(Dsp dsp) {
        this.dsp = dsp;
        return this;
    }

    /**
     * Get dsp
     *
     * @return dsp
     */
    @Valid
    public Dsp getDsp() {
        return dsp;
    }

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

    public PublicPlaylist dspPlaylistId(String dspPlaylistId) {
        this.dspPlaylistId = dspPlaylistId;
        return this;
    }

    /**
     * Playlist object identifier used by the related Dsp.
     *
     * @return dspPlaylistId
     */
    public String getDspPlaylistId() {
        return dspPlaylistId;
    }

    public void setDspPlaylistId(String dspPlaylistId) {
        this.dspPlaylistId = dspPlaylistId;
    }

    public PublicPlaylist amazonPlaylistId(String amazonPlaylistId) {
        this.amazonPlaylistId = amazonPlaylistId;
        return this;
    }

    /**
     * Playlist object identifier used by Amazon (may not be available).
     *
     * @return amazonPlaylistId
     */
    public String getAmazonPlaylistId() {
        return amazonPlaylistId;
    }

    public void setAmazonPlaylistId(String amazonPlaylistId) {
        this.amazonPlaylistId = amazonPlaylistId;
    }

    public PublicPlaylist followers(Integer followers) {
        this.followers = followers;
        return this;
    }

    /**
     * Get followers
     *
     * @return followers
     */
    public Integer getFollowers() {
        return followers;
    }

    public void setFollowers(Integer followers) {
        this.followers = followers;
    }

    public PublicPlaylist description(String description) {
        this.description = description;
        return this;
    }

    /**
     * Get description
     *
     * @return description
     */
    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public PublicPlaylist ignored(Boolean ignored) {
        this.ignored = ignored;
        return this;
    }

    /**
     * Get ignored
     *
     * @return ignored
     */
    public Boolean isIgnored() {
        return ignored;
    }

    public void setIgnored(Boolean ignored) {
        this.ignored = ignored;
    }

    public PublicPlaylist removed(Boolean removed) {
        this.removed = removed;
        return this;
    }

    /**
     * Get removed
     *
     * @return removed
     */
    public Boolean isRemoved() {
        return removed;
    }

    public void setRemoved(Boolean removed) {
        this.removed = removed;
    }

    public PublicPlaylist personalised(Boolean personalised) {
        this.personalised = personalised;
        return this;
    }

    /**
     * Get personalised
     *
     * @return personalised
     */
    public Boolean isPersonalised() {
        return personalised;
    }

    public void setPersonalised(Boolean personalised) {
        this.personalised = personalised;
    }

    public PublicPlaylist publicPlaylist(Boolean publicPlaylist) {
        this.publicPlaylist = publicPlaylist;
        return this;
    }

    /**
     * Get publicPlaylist
     *
     * @return publicPlaylist
     */
    public Boolean isPublicPlaylist() {
        return publicPlaylist;
    }

    public void setPublicPlaylist(Boolean publicPlaylist) {
        this.publicPlaylist = publicPlaylist;
    }

    public PublicPlaylist owner(PlaylistOwner owner) {
        this.owner = owner;
        return this;
    }

    /**
     * Get owner
     *
     * @return owner
     */
    public PlaylistOwner getOwner() {
        return owner;
    }

    public void setOwner(PlaylistOwner owner) {
        this.owner = owner;
    }

    public PublicPlaylist image(ImageSimple image) {
        this.image = JsonNullable.of(image);
        return this;
    }

    public boolean isSaveHistory() {
        return saveHistory;
    }

    public boolean isSaveTrackList() {
        return saveTrackList;
    }

    public PublicPlaylist saveHistory(boolean saveHistory) {
        this.saveHistory = saveHistory;
        return this;
    }

    public PublicPlaylist saveTrackList(boolean saveTrackList) {
        this.saveTrackList = saveTrackList;
        return this;
    }

    /**
     * Get image
     *
     * @return image
     */
    @Valid
    public JsonNullable<ImageSimple> getImage() {
        return image;
    }

    public void setImage(JsonNullable<ImageSimple> image) {
        this.image = image;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        PublicPlaylist playlist = (PublicPlaylist) o;
        return Objects.equals(this.name, playlist.name) &&
            Objects.equals(this.playlistId, playlist.playlistId) &&
            Objects.equals(this.uri, playlist.uri) &&
            Objects.equals(this.numTracks, playlist.numTracks) &&
            Objects.equals(this.countryCode, playlist.countryCode) &&
            Objects.equals(this.rank, playlist.rank) &&
            Objects.equals(this.dsp, playlist.dsp) &&
            Objects.equals(this.dspPlaylistId, playlist.dspPlaylistId) &&
            Objects.equals(this.amazonPlaylistId, playlist.amazonPlaylistId) &&
            Objects.equals(this.image, playlist.image) &&
            Objects.equals(this.followers, playlist.followers) &&
            Objects.equals(this.ignored, playlist.ignored) &&
            Objects.equals(this.removed, playlist.removed) &&
            Objects.equals(this.personalised, playlist.personalised) &&
            Objects.equals(this.publicPlaylist, playlist.publicPlaylist) &&
            Objects.equals(this.owner, playlist.owner);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, playlistId, uri, numTracks, countryCode, rank, dsp, dspPlaylistId,
            amazonPlaylistId, image,
            followers, ignored, removed, personalised, publicPlaylist, owner);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("class PublicPlaylist {\n");

        sb.append("    name: ").append(toIndentedString(name)).append("\n");
        sb.append("    playlistId: ").append(toIndentedString(playlistId)).append("\n");
        sb.append("    uri: ").append(toIndentedString(uri)).append("\n");
        sb.append("    numTracks: ").append(toIndentedString(numTracks)).append("\n");
        sb.append("    countryCode: ").append(toIndentedString(countryCode)).append("\n");
        sb.append("    rank: ").append(toIndentedString(rank)).append("\n");
        sb.append("    dsp: ").append(toIndentedString(dsp)).append("\n");
        sb.append("    dspPlaylistId: ").append(toIndentedString(dspPlaylistId)).append("\n");
        sb.append("    amazonPlaylistId: ").append(toIndentedString(amazonPlaylistId)).append("\n");
        sb.append("    image: ").append(toIndentedString(image)).append("\n");
        sb.append("    followers: ").append(toIndentedString(followers)).append("\n");
        sb.append("    ignored: ").append(toIndentedString(ignored)).append("\n");
        sb.append("    removed: ").append(toIndentedString(removed)).append("\n");
        sb.append("    personalised: ").append(toIndentedString(personalised)).append("\n");
        sb.append("    publicPlaylist: ").append(toIndentedString(publicPlaylist)).append("\n");
        sb.append("    owner: ").append(toIndentedString(owner)).append("\n");
        sb.append("}");
        return sb.toString();
    }

    /**
     * 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    ");
    }
}
