package com.battlelancer.seriesguide.jobs;

import static com.battlelancer.seriesguide.jobs.episodes.JobAction.EPISODE_COLLECTION;
import static com.battlelancer.seriesguide.jobs.episodes.JobAction.EPISODE_WATCHED_FLAG;

import android.content.Context;
import androidx.annotation.NonNull;
import com.battlelancer.seriesguide.backend.HexagonTools;
import com.battlelancer.seriesguide.jobs.episodes.JobAction;
import com.battlelancer.seriesguide.provider.SgRoomDatabase;
import com.battlelancer.seriesguide.sync.HexagonEpisodeSync;
import com.battlelancer.seriesguide.sync.NetworkJobProcessor;
import com.battlelancer.seriesguide.ui.episodes.EpisodeTools;
import com.battlelancer.seriesguide.util.Errors;
import com.google.api.client.http.HttpResponseException;
import com.uwetrottmann.seriesguide.backend.episodes.Episodes;
import com.uwetrottmann.seriesguide.backend.episodes.model.Episode;
import com.uwetrottmann.seriesguide.backend.episodes.model.EpisodeList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class HexagonEpisodeJob extends BaseNetworkEpisodeJob {

    @NonNull private final HexagonTools hexagonTools;

    public HexagonEpisodeJob(@NonNull HexagonTools hexagonTools, JobAction action,
            SgJobInfo jobInfo) {
        super(action, jobInfo);
        this.hexagonTools = hexagonTools;
    }

    @NonNull
    @Override
    public NetworkJobProcessor.JobResult execute(Context context) {
        int showTvdbIdOrZero = SgRoomDatabase.getInstance(context).sgShow2Helper()
                .getShowTvdbId(jobInfo.showId());
        if (showTvdbIdOrZero <= 0) {
            // Can't run this job (for now), report error and remove.
            return buildResult(context, NetworkJob.ERROR_HEXAGON_CLIENT);
        }

        EpisodeList uploadWrapper = new EpisodeList();
        uploadWrapper.setShowTvdbId(showTvdbIdOrZero);

        // upload in small batches
        List<Episode> smallBatch = new ArrayList<>();
        final List<Episode> episodes = getEpisodesForHexagon();
        while (!episodes.isEmpty()) {
            // batch small enough?
            if (episodes.size() <= HexagonEpisodeSync.MAX_BATCH_SIZE) {
                smallBatch = episodes;
            } else {
                // build smaller batch
                for (int count = 0; count < HexagonEpisodeSync.MAX_BATCH_SIZE; count++) {
                    if (episodes.isEmpty()) {
                        break;
                    }
                    smallBatch.add(episodes.remove(0));
                }
            }

            // upload
            uploadWrapper.setEpisodes(smallBatch);

            try {
                Episodes episodesService = hexagonTools.getEpisodesService();
                if (episodesService == null) {
                    return buildResult(context, NetworkJob.ERROR_HEXAGON_AUTH);
                }
                episodesService.save(uploadWrapper).execute();
            } catch (HttpResponseException e) {
                Errors.logAndReportHexagon("save episodes", e);
                int code = e.getStatusCode();
                if (code >= 400 && code < 500) {
                    return buildResult(context, NetworkJob.ERROR_HEXAGON_CLIENT);
                } else {
                    return buildResult(context, NetworkJob.ERROR_HEXAGON_SERVER);
                }
            } catch (IOException | IllegalArgumentException e) {
                // Note: JSON parser may throw IllegalArgumentException.
                Errors.logAndReportHexagon("save episodes", e);
                return buildResult(context, NetworkJob.ERROR_CONNECTION);
            }

            // prepare for next batch
            smallBatch.clear();
        }

        return buildResult(context, NetworkJob.SUCCESS);
    }

    /**
     * Builds a list of episodes ready to upload to hexagon. However, the show TVDb id is not set.
     * It should be set in a wrapping {@link com.uwetrottmann.seriesguide.backend.episodes.model.EpisodeList}.
     */
    @NonNull
    private List<Episode> getEpisodesForHexagon() {
        boolean isWatchedNotCollected;
        if (action == EPISODE_WATCHED_FLAG) {
            isWatchedNotCollected = true;
        } else if (action == EPISODE_COLLECTION) {
            isWatchedNotCollected = false;
        } else {
            throw new IllegalArgumentException("Action " + action + " not supported.");
        }

        List<Episode> episodes = new ArrayList<>();
        for (int i = 0; i < jobInfo.episodesLength(); i++) {
            EpisodeInfo episodeInfo = jobInfo.episodes(i);

            Episode episode = new Episode();
            episode.setSeasonNumber(episodeInfo.season());
            episode.setEpisodeNumber(episodeInfo.number());
            if (isWatchedNotCollected) {
                episode.setWatchedFlag(jobInfo.flagValue());
                // Always upload (regardless if watched, skipped or not watched).
                // Also ensures legacy data slowly adds the new plays field.
                episode.setPlays(episodeInfo.plays());
            } else {
                episode.setIsInCollection(EpisodeTools.isCollected(jobInfo.flagValue()));
            }

            episodes.add(episode);
        }
        return episodes;
    }
}

@@ -871,24 +871,31 @@
 sodes.model.
+SgCloud
 Episode;%0Aimp
@@ -942,32 +942,39 @@
 .episodes.model.
+SgCloud
 EpisodeList;%0Aimp
@@ -1474,25 +1474,25 @@
    int showT
-v
+m
 dbIdOrZero =
@@ -1565,25 +1565,25 @@
    .getShowT
-v
+m
 dbId(jobInfo
@@ -1607,25 +1607,25 @@
    if (showT
-v
+m
 dbIdOrZero %3C
@@ -1790,24 +1790,31 @@
  %7D%0A%0A        
+SgCloud
 EpisodeList 
@@ -1829,24 +1829,31 @@
 apper = new 
+SgCloud
 EpisodeList(
@@ -1881,25 +1881,25 @@
 per.setShowT
-v
+m
 dbId(showTvd
@@ -1896,17 +1896,17 @@
 Id(showT
-v
+m
 dbIdOrZe
@@ -1951,32 +1951,39 @@
 es%0A        List%3C
+SgCloud
 Episode%3E smallBa
@@ -2022,24 +2022,31 @@
  final List%3C
+SgCloud
 Episode%3E epi
@@ -2951,16 +2951,26 @@
 ice.save
+SgEpisodes
 (uploadW
@@ -3929,13 +3929,8 @@
 how 
-TVDb 
 id i
@@ -3967,17 +3967,19 @@
  set in 
-a
+the
  wrappin
@@ -3984,79 +3984,14 @@
 ing 
-%7B@link com.uwetrottmann.seriesguide.backend.episodes.model.EpisodeList%7D
+entity
 .%0A  
@@ -4022,24 +4022,31 @@
 rivate List%3C
+SgCloud
 Episode%3E get
@@ -4418,16 +4418,23 @@
    List%3C
+SgCloud
 Episode%3E
@@ -4593,24 +4593,31 @@
             
+SgCloud
 Episode epis
@@ -4626,16 +4626,23 @@
 e = new 
+SgCloud
 Episode(

send watched/collected updates using TMDB ID.