/*
 * (c) 2021 Pluto Inc.
 */
package tv.pluto.mongo.utils.episodes;

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.GsonBuilder;
import com.plutotv.test.lib.LogUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import tv.pluto.mongo.entities.episodes.AgeRange;
import tv.pluto.mongo.entities.episodes.AgeRangeEpisode;
import tv.pluto.mongo.entities.episodes.Episodes;
import tv.pluto.mongo.utils.MongoUtils;
import tv.pluto.mongo.utils.json.SkipDeserialisation;

public class AgeRangeEpisodeUtils extends MongoUtils {

  public EpisodeUtils episodeUtils = new EpisodeUtils();
  public AgeRangeUtils ageRangeUtils = new AgeRangeUtils();

  public AgeRangeEpisodeUtils() {
    this.genericCollection = this.collectionAgeRangeEpisode;
    this.gson =
        new GsonBuilder()
            .addDeserializationExclusionStrategy(
                new ExclusionStrategy() {
                  @Override
                  public boolean shouldSkipField(FieldAttributes f) {
                    return f.getAnnotation(SkipDeserialisation.class) != null;
                  }

                  @Override
                  public boolean shouldSkipClass(Class<?> clazz) {
                    return false;
                  }
                })
            .setPrettyPrinting()
            .create();
  }

  public AgeRangeEpisode getAgeRangePopulatedEpisodeById(String id) {
    Episodes episode = episodeUtils.getDocumentById(id);

    if (episode != null) {
      AgeRangeEpisode result = this.mapGrpcObjectToPojo(episode, AgeRangeEpisode.class);
      return populateEpisode(Collections.singletonList(result)).get(0);
    } else {
      LogUtils.logWarningMessage("Could not find Episode by ID.");
      return null;
    }
  }

  public AgeRangeEpisode getAgeRangePopulatedEpisodeById(String id, String[] fields) {
    Episodes episode = episodeUtils.getDocumentById(id, fields);

    if (episode == null) {
      LogUtils.logWarningMessage("Could not find Episode by ID.");
      return null;
    }

    AgeRangeEpisode partialResult = this.mapGrpcObjectToPojo(episode, AgeRangeEpisode.class);

    return populateEpisode(Collections.singletonList(partialResult), true).get(0);
  }

  public List<AgeRangeEpisode> getAgeRangePopulatedEpisodeByIds(List<String> ids) {
    List<Episodes> episode = episodeUtils.getDocumentsByIds(ids);

    if (episode != null) {
      List<AgeRangeEpisode> result = this.mapGrpcObjectsToPojo(episode, AgeRangeEpisode.class);
      return populateEpisode(result);
    } else {
      LogUtils.logWarningMessage("Could not find Episode by ID.");
      return null;
    }
  }

  public AgeRangeEpisode replaceAgeRange(
      AgeRangeEpisode ageRangeEpisode, Map<String, AgeRange> ageRangeMap) {
    Episodes episode = episodeUtils.getDocumentById(ageRangeEpisode.getId());
    List<String> ageRangeIds = transformIdsToStrings(episode.getAgeRange());

    AgeRangeEpisode result = this.mapGrpcObjectToPojo(episode, AgeRangeEpisode.class);

    List<AgeRange> ageRangeList = new ArrayList<>();

    if (!ageRangeIds.isEmpty()) {
      ageRangeIds.forEach(
          id -> {
            if (ageRangeMap.containsKey(id)) {
              ageRangeList.add(ageRangeMap.get(id));
            }
          });

      if (ageRangeList.isEmpty()) {
        LogUtils.logWarningMessage(
            "ageRange Id(s) on episode does not match an ageRange from the DB table/collection.");
      }
    }

    result.setAgeRange(ageRangeList);

    return result;
  }

  public List<AgeRangeEpisode> populateEpisode(List<AgeRangeEpisode> ageRangeEpisodes) {
    List<String> ids =
        ageRangeEpisodes.stream().map(AgeRangeEpisode::getId).collect(Collectors.toList());

    List<Episodes> episodes = episodeUtils.getDocumentsByIds(ids);

    Map<String, AgeRange> ageRangeMap = ageRangeUtils.getAgeRangeMapForEpisodes(episodes);

    if (ageRangeMap.isEmpty()) {
      LogUtils.logWarningMessage(
          "ageRange Id(s) on episode does not match an ageRange from the DB table/collection.");
      return ageRangeEpisodes;
    }

    return ageRangeEpisodes.stream()
        .map(episode -> replaceAgeRange(episode, ageRangeMap))
        .collect(Collectors.toList());
  }

  public List<AgeRangeEpisode> populateEpisode(
      List<AgeRangeEpisode> ageRangeEpisodes, boolean skipDbFetch) {
    List<Episodes> episodes;

    if (skipDbFetch) {
      episodes =
          ageRangeEpisodes.stream()
              .map(
                  ageRangeEpisode -> {
                    Episodes ep = new Episodes();
                    ep.setId(ageRangeEpisode.getId());
                    return ep;
                  })
              .collect(Collectors.toList());
    } else {
      List<String> ids =
          ageRangeEpisodes.stream().map(AgeRangeEpisode::getId).collect(Collectors.toList());
      episodes = episodeUtils.getDocumentsByIds(ids);
    }

    Map<String, AgeRange> ageRangeMap = ageRangeUtils.getAgeRangeMapForEpisodes(episodes);

    if (ageRangeMap.isEmpty()) {
      LogUtils.logWarningMessage(
          "ageRange Id(s) on episode does not match an ageRange from the DB table/collection.");
      return ageRangeEpisodes;
    }

    return ageRangeEpisodes.stream()
        .map(episode -> replaceAgeRange(episode, ageRangeMap))
        .collect(Collectors.toList());
  }
}
