/*
 * (c) 2020 Pluto Inc.
 */
package tv.pluto.grpc.episodes;

import static tv.pluto.ip.dal.episode.Episodes.CreateRequest;
import static tv.pluto.ip.dal.episode.Episodes.CreateResponse;
import static tv.pluto.ip.dal.episode.Episodes.DeleteByIdRequest;
import static tv.pluto.ip.dal.episode.Episodes.DeleteByIdResponse;
import static tv.pluto.ip.dal.episode.Episodes.DeleteByIdsRequest;
import static tv.pluto.ip.dal.episode.Episodes.DeleteByIdsResponse;
import static tv.pluto.ip.dal.episode.Episodes.Episode;
import static tv.pluto.ip.dal.episode.Episodes.GetAgeRangePopulatedEpisodeByIdRequest;
import static tv.pluto.ip.dal.episode.Episodes.GetAgeRangePopulatedEpisodeByIdResponse;
import static tv.pluto.ip.dal.episode.Episodes.GetAgeRangePopulatedEpisodesByIdsRequest;
import static tv.pluto.ip.dal.episode.Episodes.GetAgeRangePopulatedEpisodesByIdsResponse;
import static tv.pluto.ip.dal.episode.Episodes.GetAllPagedRequest;
import static tv.pluto.ip.dal.episode.Episodes.GetAllPagedSort;
import static tv.pluto.ip.dal.episode.Episodes.GetByIdRequest;
import static tv.pluto.ip.dal.episode.Episodes.GetByIdResponse;
import static tv.pluto.ip.dal.episode.Episodes.GetByIdsRequest;
import static tv.pluto.ip.dal.episode.Episodes.GetByIdsResponse;
import static tv.pluto.ip.dal.episode.Episodes.GetChannelLibraryRequest;
import static tv.pluto.ip.dal.episode.Episodes.GetChannelLibraryResponse;
import static tv.pluto.ip.dal.episode.Episodes.GetDocumentsUpdatedBetweenDatesRequest;
import static tv.pluto.ip.dal.episode.Episodes.GetEpisodeIdsBySeriesIdsRequest;
import static tv.pluto.ip.dal.episode.Episodes.GetEpisodeIdsBySeriesIdsResponse;
import static tv.pluto.ip.dal.episode.Episodes.GetEpisodesByClipIdRequest;
import static tv.pluto.ip.dal.episode.Episodes.GetEpisodesByClipIdResponse;
import static tv.pluto.ip.dal.episode.Episodes.GetPopulatedEpisodesByClipIdRequest;
import static tv.pluto.ip.dal.episode.Episodes.GetPopulatedEpisodesByClipIdResponse;
import static tv.pluto.ip.dal.episode.Episodes.UpdateContentMamUniqueIdsByIdsRequest;
import static tv.pluto.ip.dal.episode.Episodes.UpdateContentMamUniqueIdsByIdsResponse;

import com.plutotv.test.lib.LogUtils;
import io.grpc.Channel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.testng.Assert;
import tv.pluto.grpc.GenericGrpcClient;
import tv.pluto.ip.dal.episode.EpisodeManagerGrpc;
import tv.pluto.ip.dal.episode.Episodes;
import tv.pluto.mongo.entities.common.ObjectIdContentIdSet;

/** Class represents gRPC client for <b>episodes.proto</b> file */
public class EpisodesClient extends GenericGrpcClient {
  private static final Logger logger = Logger.getLogger(EpisodesClient.class.getName());

  private EpisodeManagerGrpc.EpisodeManagerBlockingStub blockingStub;

  private final GetAllPagedRequest.Builder getAllPagedBuilder = GetAllPagedRequest.newBuilder();

  public EpisodesClient(Channel channel, boolean auth) {
    blockingStub = EpisodeManagerGrpc.newBlockingStub(channel);

    if (auth) blockingStub = blockingStub.withInterceptors(CLIENT_INTERCEPTOR_AUTH);
    else blockingStub = blockingStub.withInterceptors(CLIENT_INTERCEPTOR);

    this.getDocumentsUpdatedBetweenDatesRequestBuilder =
        GetDocumentsUpdatedBetweenDatesRequest.newBuilder();
    this.getCFeedsAllIdsBuilder = Episodes.CFeedsSyncAllIdsRequest.newBuilder();
    this.getCFeedsSyncDocumentsBuilder = Episodes.CFeedsSyncDocumentsRequest.newBuilder();
  }

  public EpisodesClient(Channel channel) {
    // no Metadaata
    blockingStub = EpisodeManagerGrpc.newBlockingStub(channel);
    this.getDocumentsUpdatedBetweenDatesRequestBuilder =
        GetDocumentsUpdatedBetweenDatesRequest.newBuilder();
    this.getCFeedsAllIdsBuilder = Episodes.CFeedsSyncAllIdsRequest.newBuilder();
    this.getCFeedsSyncDocumentsBuilder = Episodes.CFeedsSyncDocumentsRequest.newBuilder();
  }

  /**
   * Method represents gRPC method GetEpisodeById from <b>episodes.proto</b> file
   *
   * @param id id of the episode
   */
  public GetByIdResponse getEpisodeById(String id) {
    logger.info("Getting info for episode with id: " + id + " ...");
    GetByIdRequest request = GetByIdRequest.newBuilder().setId(id).build();

    return blockingStub.getById(request);
  }

  public GetByIdResponse getEpisodeById(String id, List<String> projections) {
    logger.info("Getting info for episode with id: " + id + " ...");
    GetByIdRequest request =
        GetByIdRequest.newBuilder().setId(id).addAllProjection(projections).build();

    return blockingStub.getById(request);
  }

  public Episodes.GetPopulatedEpisodeByIdResponse getPopulatedEpisodeById(
      String id, String populate) {
    logger.info("Getting info for episode with id: " + id + " ...");
    Episodes.GetPopulatedEpisodeByIdRequest.Builder builder =
        Episodes.GetPopulatedEpisodeByIdRequest.newBuilder();

    if (!id.isEmpty()) builder.setId(id);

    if (!populate.isEmpty()) {
      Episodes.PopulatePath populatePath =
          Episodes.PopulatePath.newBuilder().setPath(populate).build();
      builder.addPopulate(populatePath);
    }

    return blockingStub.getPopulatedEpisodeById(builder.build());
  }

  public Episodes.GetPopulatedEpisodesByIdsResponse getPopulatedEpisodesByIds(
      List<String> ids, String populate) {
    logger.info("Getting info for episode with id: " + ids + " ...");
    Episodes.GetPopulatedEpisodesByIdsRequest.Builder builder =
        Episodes.GetPopulatedEpisodesByIdsRequest.newBuilder();

    if (!ids.isEmpty()) builder.addAllIds(ids);

    if (!populate.isEmpty()) {
      Episodes.PopulatePath populatePath =
          Episodes.PopulatePath.newBuilder().setPath(populate).build();
      builder.addPopulate(populatePath);
    }

    return blockingStub.getPopulatedEpisodesByIds(builder.build());
  }

  /**
   * Method represents gRPC method GetEpisodesByIds from <b>episodes.proto</b> file
   *
   * @param episodesIds List of clips ids to search
   */
  public GetByIdsResponse getByIds(List<String> episodesIds) {
    logger.info("Getting episodes for ids: " + episodesIds + " ...");

    GetByIdsRequest request = GetByIdsRequest.newBuilder().addAllIds(episodesIds).build();

    return blockingStub.getByIds(request);
  }

  public GetByIdsResponse getByIds(List<String> episodesIds, List<String> projections) {
    logger.info("Getting episodes for ids: " + episodesIds + " ...");

    GetByIdsRequest request =
        GetByIdsRequest.newBuilder().addAllIds(episodesIds).addAllProjection(projections).build();

    return blockingStub.getByIds(request);
  }

  public Episodes.GetEpisodesByIdsResponse getEpisodesByIds(List<String> episodesIds) {
    logger.info("Getting episodes for ids: " + episodesIds + " ...");

    Episodes.GetEpisodesByIdsRequest request =
        Episodes.GetEpisodesByIdsRequest.newBuilder().addAllIds(episodesIds).build();

    return blockingStub.getEpisodesByIds(request);
  }

  public GetEpisodesByClipIdResponse getEpisodesByClipId(GetEpisodesByClipIdRequest request) {
    return blockingStub.getEpisodesByClipId(request);
  }

  public GetAgeRangePopulatedEpisodeByIdResponse getAgeRangePopulatedEpisodeById(
      GetAgeRangePopulatedEpisodeByIdRequest request) {
    return blockingStub.getAgeRangePopulatedEpisodeById(request);
  }

  /**
   * Method represents gRPC method GetAgeRangePopulatedEpisodesByIds from <b>episodes.proto</b> file
   *
   * @param episodesIds List of clips ids to search
   */
  public GetAgeRangePopulatedEpisodesByIdsResponse getAgeRangePopulatedEpisodesByIds(
      List<String> episodesIds) {
    logger.info("Getting getAgeRangePopulatedEpisodesByIds for ids: " + episodesIds + " ...");

    GetAgeRangePopulatedEpisodesByIdsRequest request =
        GetAgeRangePopulatedEpisodesByIdsRequest.newBuilder().addAllIds(episodesIds).build();

    return blockingStub.getAgeRangePopulatedEpisodesByIds(request);
  }

  public UpdateContentMamUniqueIdsByIdsResponse updateContentMamUniqueIdsByIds(
      List<ObjectIdContentIdSet> objectIdContentIdSets) {
    LogUtils.logInfoMessage("Updating episodes contentUUID for " + objectIdContentIdSets);

    UpdateContentMamUniqueIdsByIdsRequest.Builder builder =
        UpdateContentMamUniqueIdsByIdsRequest.newBuilder();

    objectIdContentIdSets.forEach(
        value ->
            builder.addObjectIdContentIdSets(
                Episodes.ObjectIdContentIdSet.newBuilder()
                    .setId(value.getId())
                    .setContentUUID(value.getContentUUID())
                    .build()));

    return blockingStub.updateContentMamUniqueIdsByIds(builder.build());
  }

  public CreateResponse create(Episode episode) {
    return blockingStub.create(CreateRequest.newBuilder().setDocument(episode).build());
  }

  public Episode getNewEpisodeBuild() {
    String testDal = "TestDal";
    String customId = "622a425555a07a001bfee955";

    Episodes.CustomReferenceValue deliveryPartner =
        Episodes.CustomReferenceValue.newBuilder()
            .setId(customId)
            .setType("deliveryPartner")
            .setValue(customId)
            .build();

    return Episode.newBuilder()
        .setName("Rory Scovel: Live Without Fear")
        .setDescription(testDal)
        .setSummary(testDal)
        .setMetadataLanguage(testDal)
        .setSubGenre("Cartoons")
        .setGenre("Children & Family")
        .setSubCategory("Cartoons")
        .setCategory("Pop Culture")
        .setAllotment(5)
        .addTags("us")
        .addCustomReferences(deliveryPartner)
        .build();
  }

  public DeleteByIdResponse deleteById(String id) {
    DeleteByIdRequest request = DeleteByIdRequest.newBuilder().setId(id).build();

    return blockingStub.deleteById(request);
  }

  public void deleteEpisodeWithAssertion(String id) {
    DeleteByIdRequest request = DeleteByIdRequest.newBuilder().setId(id).build();
    LogUtils.logInfoMessage("Deleting episode with id: " + id);
    Assert.assertEquals(1, blockingStub.deleteById(request).getDeletedCount());
  }

  public DeleteByIdsResponse deleteByIds(List<String> ids) {
    DeleteByIdsRequest request = DeleteByIdsRequest.newBuilder().addAllIds(ids).build();

    return blockingStub.deleteByIds(request);
  }

  public GetPopulatedEpisodesByClipIdResponse getPopulatedEpisodesByClipId(
      String id,
      int limit,
      int offset,
      List<GetAllPagedSort> sort,
      List<tv.pluto.ip.dal.episode.Episodes.Filter> listFilter) {
    GetPopulatedEpisodesByClipIdRequest.Builder populatedBuilder =
        GetPopulatedEpisodesByClipIdRequest.newBuilder()
            .setId(id)
            .setLimit(limit)
            .setOffset(offset);

    if (sort != null) {
      populatedBuilder.addAllSort(sort);
    }

    if (listFilter != null) {
      Episodes.Query.Builder listBuilder = Episodes.Query.newBuilder().addAllFilter(listFilter);
      populatedBuilder.setQuery(listBuilder);
    }

    GetPopulatedEpisodesByClipIdRequest request = populatedBuilder.build();
    return blockingStub.getPopulatedEpisodesByClipId(request);
  }

  public Iterator<Episode> getAllResponse(String region) {
    Episodes.GetAllRequest getAllRequest =
        Episodes.GetAllRequest.newBuilder().setRegion(region).build();
    return blockingStub.getAll(getAllRequest);
  }

  public GetEpisodeIdsBySeriesIdsResponse getEpisodeIdsBySeriesIds(
      List<String> ids, String distinctValue) {
    GetEpisodeIdsBySeriesIdsRequest.Builder builder = GetEpisodeIdsBySeriesIdsRequest.newBuilder();

    if (ids != null) builder.addAllIds(ids);

    if (distinctValue != null) builder.setDistinct(distinctValue);

    return blockingStub.getEpisodeIdsBySeriesIds(builder.build());
  }

  public Episodes.Filter getFilterEpisode(
      String field, String value, String type, String operator) {
    Episodes.Filter.Builder filterBuild =
        Episodes.Filter.newBuilder().setField(field).setValue(value).setType(type);

    if (operator != null) filterBuild.setOperator(operator);

    return filterBuild.build();
  }

  public List<GetAllPagedSort> grpcSortAllPagedSortList(String field, int value) {
    return Collections.singletonList(
        GetAllPagedSort.newBuilder().setField(field).setValue(value).build());
  }

  public Episode setAdPodDuration(Episode episodeGrpc) {
    Episodes.SetAdPodDurationsRequest request =
        Episodes.SetAdPodDurationsRequest.newBuilder().setDocument(episodeGrpc).build();
    return blockingStub.setAdPodDurations(request);
  }

  public Episode calculateTotalAdPodsDuration(Episode episodeGrpc) {
    Episodes.CalculateTotalAdPodsDurationRequest request =
        Episodes.CalculateTotalAdPodsDurationRequest.newBuilder().setDocument(episodeGrpc).build();
    return blockingStub.calculateTotalAdPodsDuration(request);
  }

  public Episodes.UpdateFieldsResponse getUpdateFields(
      String randomEpisodeId, List<String> updatedFields, Episode episode) {
    Episodes.UpdateFieldsRequest request =
        Episodes.UpdateFieldsRequest.newBuilder()
            .setId(randomEpisodeId)
            .setDocument(episode)
            .addAllUpdateFields(updatedFields)
            .build();
    return blockingStub.updateFields(request);
  }

  public Episodes.GetChannelLibraryResponse getChannelLibrary(
      Episodes.GetChannelLibraryRequest request) {
    return blockingStub.getChannelLibrary(request);
  }

  public Episodes.GetAllIdsResponse getAllIds(
      Boolean isPublished, Integer limit, Integer offset, Boolean totalCount, Boolean vod) {
    Episodes.GetAllIdsRequest.Builder builder = Episodes.GetAllIdsRequest.newBuilder();
    if (isPublished != null) builder.setPublished(isPublished);

    if (limit != null) builder.setLimit(limit);

    if (offset != null) builder.setOffset(offset);

    if (totalCount != null) builder.setDisableTotalCount(totalCount);

    if (vod != null) builder.setVod(vod);

    return blockingStub.getAllIds(builder.build());
  }

  public GetChannelLibraryResponse getChannelLibrary(
      tv.pluto.mongo.entities.episodes.get_channel_library.GetChannelLibraryRequest request) {
    GetChannelLibraryRequest.Builder builder = GetChannelLibraryRequest.newBuilder();

    if (!request.getChannelId().isEmpty()) {
      builder.setChannelId(request.getChannelId());
    }

    if (!request.getName().isEmpty()) {
      builder.setName(request.getName());
    }

    if (request.getStart() != 0) {
      builder.setStart(request.getStart());
    }

    if (request.getEnd() != 0) {
      builder.setEnd(request.getEnd());
    }

    if (!request.getStatus().isEmpty()) {
      builder.addAllStatus(request.getStatus());
    }

    if (!request.getRating().isEmpty()) {
      builder.setRating(request.getRating());
    }

    if (!request.getGenre().isEmpty()) {
      builder.setGenre(request.getGenre());
    }

    if (request.getSeason() != 0) {
      builder.setSeason(request.getSeason());
    }

    if (request.getNumber() != 0) {
      builder.setNumber(request.getNumber());
    }

    if (!request.getAgeRange().isEmpty()) {
      builder.setAgeRange(request.getAgeRange());
    }

    if (!request.getTmsid().isEmpty()) {
      builder.setTmsid(request.getTmsid());
    }

    if (request.getLimit() != 0) {
      builder.setLimit(request.getLimit());
    }

    if (request.getOffset() != 0) {
      builder.setOffset(request.getOffset());
    }

    if (!request.getSort().isEmpty()) {
      builder.addAllSort(request.getSort());
    }

    return blockingStub.getChannelLibrary(builder.build());
  }

  public Episodes.GetEpisodeUsingExternalIdResponse getEpisodesUsingExternalId(
      Episodes.GetEpisodeUsingExternalIdRequest request) {
    return blockingStub.getEpisodeUsingExternalId(request);
  }

  public Episodes.UnsetNextTimelineResponse unsetNextTimeline(
      List<String> idsToRemove, String channelId, String maxStart) {

    Episodes.UnsetNextTimelineRequest.Builder builder =
        Episodes.UnsetNextTimelineRequest.newBuilder();

    if (idsToRemove != null && !idsToRemove.isEmpty()) {
      builder.addAllIdsToRemove(idsToRemove); // Add list of IDs
    }

    if (channelId != null && !channelId.isEmpty()) {
      builder.setChannelId(channelId);
    }

    if (maxStart != null && !maxStart.isEmpty()) {
      builder.setMaxStart(maxStart);
    }

    return blockingStub.unsetNextTimeline(builder.build());
  }

  public tv.pluto.ip.dal.episode.Episodes.Filter episodeFilterBody(
      String field, String value, String type) {

    return tv.pluto.ip.dal.episode.Episodes.Filter.newBuilder()
        .setField(field)
        .setValue(value)
        .setType(type)
        .build();
  }

  public List<tv.pluto.ip.dal.episode.Episodes.Filter> filtersBodyInEpisodes(
      tv.pluto.mongo.entities.episodes.Episodes episode) {
    List<String> listFilters =
        Stream.of("name", "series", "activeRegion", "nextTimeline.start")
            .collect(Collectors.toList());

    List<tv.pluto.ip.dal.episode.Episodes.Filter> filtersInEpisodes = new ArrayList<>();

    listFilters.forEach(
        field -> {
          String value = null;

          switch (field) {
            case "name":
              value = episode.getName();
              break;
            case "series":
              value = episode.getSeries();
              break;
            case "activeRegion":
              value = episode.getActiveRegion();
              break;
            case "nextTimeline.start":
              if (episode.getNextTimeline() != null
                  && episode.getNextTimeline().getStart() != null) {
                value = episode.getNextTimeline().getStart().toString();
              }
              break;
          }

          // Ensure value is not null and not "0"
          if (value != null && !value.equals("0")) {
            // Directly assigning "string" as the type
            tv.pluto.ip.dal.episode.Episodes.Filter episodeFilterBody =
                episodeFilterBody(field, value, "string");
            filtersInEpisodes.add(episodeFilterBody);
          }
        });

    return filtersInEpisodes;
  }

  public tv.pluto.ip.dal.episode.Episodes.GetAllPagedResponse getAllPagedResponse(
      List<tv.pluto.ip.dal.episode.Episodes.Filter> listFiltersBodyInEpisodes) {
    tv.pluto.ip.dal.episode.Episodes.GetAllPagedRequest request =
        tv.pluto.ip.dal.episode.Episodes.GetAllPagedRequest.newBuilder()
            .addAllFilters(listFiltersBodyInEpisodes)
            .build();

    return blockingStub.getAllPaged(request);
  }

  public Episodes.GetEpisodeByNameSeriesAndActiveRegionResponse
      getEpisodeByNameSeriesAndActiveRegion(
          Episodes.GetEpisodeByNameSeriesAndActiveRegionRequest request) {

    return blockingStub.getEpisodeByNameSeriesAndActiveRegion(request);
  }

  public Episodes.GetEpisodeBySeriesSeasonAndNumberResponse getEpisodeBySeriesSeasonAndNumber(
      Episodes.GetEpisodeBySeriesSeasonAndNumberRequest request) {

    return blockingStub.getEpisodeBySeriesSeasonAndNumber(request);
  }

  public Episodes.UpdateLastAiredResponse updateLastAired(String id) {
    Episodes.UpdateLastAiredRequest request =
        Episodes.UpdateLastAiredRequest.newBuilder().setEpisodeId(id).build();
    return blockingStub.updateLastAired(request);
  }
}
