/*
 * Copyright 2019 The Data Transfer Project Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.datatransferproject.transfer.spotify.playlists;


import static java.lang.String.format;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.wrapper.spotify.SpotifyApi;
import com.wrapper.spotify.exceptions.SpotifyWebApiException;
import com.wrapper.spotify.model_objects.specification.Paging;
import com.wrapper.spotify.model_objects.specification.PlaylistSimplified;
import com.wrapper.spotify.model_objects.specification.PlaylistTrack;
import com.wrapper.spotify.model_objects.specification.Track;
import com.wrapper.spotify.model_objects.specification.User;
import org.datatransferproject.api.launcher.Monitor;
import org.datatransferproject.spi.transfer.provider.ExportResult;
import org.datatransferproject.spi.transfer.provider.ExportResult.ResultType;
import org.datatransferproject.spi.transfer.provider.Exporter;
import org.datatransferproject.types.common.ExportInformation;
import org.datatransferproject.types.common.models.playlists.MusicAlbum;
import org.datatransferproject.types.common.models.playlists.MusicGroup;
import org.datatransferproject.types.common.models.playlists.MusicPlaylist;
import org.datatransferproject.types.common.models.playlists.MusicRecording;
import org.datatransferproject.types.common.models.playlists.PlaylistContainerResource;
import org.datatransferproject.types.transfer.auth.TokensAndUrlAuthData;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

/**
 * Exports playlists from Spotify.
 **/
public class SpotifyPlaylistExporter implements
    Exporter<TokensAndUrlAuthData, PlaylistContainerResource> {

  private final Monitor monitor;
  private final SpotifyApi spotifyApi;

  public SpotifyPlaylistExporter(Monitor monitor, SpotifyApi spotifyApi) {
    this.monitor = monitor;
    this.spotifyApi = spotifyApi;
  }

  @Override
  public ExportResult<PlaylistContainerResource> export(UUID jobId,
      TokensAndUrlAuthData authData, Optional<ExportInformation> exportInformation)
      throws Exception {
    spotifyApi.setAccessToken(authData.getAccessToken());
    spotifyApi.setRefreshToken(authData.getRefreshToken());

    User user = spotifyApi.getCurrentUsersProfile().build().execute();

    return new ExportResult<>(
        ResultType.END,
        enumeratePlaylists(user.getId()));
  }

  private PlaylistContainerResource enumeratePlaylists(String userId)
      throws IOException, SpotifyWebApiException {
    List<MusicPlaylist> results = new ArrayList<>();
    int offset = 0;
    Paging<PlaylistSimplified> playlists;
    do {
      int finalOffset = offset;
      monitor.debug(() -> format("Fetching playlists with offset %s", finalOffset));
      playlists = spotifyApi.getListOfUsersPlaylists(userId)
          .offset(offset)
          .build()
          .execute();
      for (PlaylistSimplified playlist : playlists.getItems()) {
        monitor.debug(
            () ->
                format(
                    "Got playlist %s: %s (id: %s)",
                    playlist.getId(), playlist.getName(), playlist.getHref()));
        results.add(new MusicPlaylist(
            playlist.getHref(),
            playlist.getName(),
            fetchPlaylist(playlist.getId())));
      }
      offset += playlists.getItems().length;
    } while (!Strings.isNullOrEmpty(playlists.getNext()) && playlists.getItems().length > 0);
    return new PlaylistContainerResource(results);
  }

  private ImmutableList<MusicRecording> fetchPlaylist(String playlistId)
      throws IOException, SpotifyWebApiException {
    int offset = 0;
    Paging<PlaylistTrack> playlistTrackResults;
    ImmutableList.Builder<MusicRecording> results = new ImmutableList.Builder<>(); 
    do {
      int finalOffset = offset;
      monitor.debug(
          () ->
              format(
                  "Fetching playlist's %s tracks with offset %s, next: %s",
                  playlistId, finalOffset));
      playlistTrackResults = spotifyApi.getPlaylistsTracks(playlistId)
          .offset(offset)
          .build()
          .execute();
      for (PlaylistTrack track : playlistTrackResults.getItems()) {
        results.add(convertTrack(track));
      }
      offset += playlistTrackResults.getItems().length;
    } while (!Strings.isNullOrEmpty(playlistTrackResults.getNext())
        && playlistTrackResults.getItems().length > 0);
    return results.build();
  }

  private MusicRecording convertTrack(PlaylistTrack playlistTrack) {
    Track track = playlistTrack.getTrack();
    monitor.debug(() -> format("Converting: %s", track));
    return new MusicRecording(
        track.getHref(),
        track.getName(),
        track.getExternalIds().getExternalIds().get("isrc"),
        new MusicAlbum(track.getAlbum().getHref(), track.getAlbum().getName()),
        new MusicGroup(track.getArtists()[0].getName()));
  }
}