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

import static tv.pluto.ip.dal.channels.Channels.BulkDeviceUpdateRequest;
import static tv.pluto.ip.dal.channels.Channels.BulkDeviceUpdateResponse;
import static tv.pluto.ip.dal.channels.Channels.BulkRemoveEpisodesFromQueueRequest;
import static tv.pluto.ip.dal.channels.Channels.CFeedsSyncDocumentsRequest;
import static tv.pluto.ip.dal.channels.Channels.CreateRequest;
import static tv.pluto.ip.dal.channels.Channels.CreateResponse;
import static tv.pluto.ip.dal.channels.Channels.Filter;
import static tv.pluto.ip.dal.channels.Channels.GetAllPagedChannelLiteRequest;
import static tv.pluto.ip.dal.channels.Channels.GetAllPagedChannelLiteResponse;
import static tv.pluto.ip.dal.channels.Channels.GetAllPagedSort;
import static tv.pluto.ip.dal.channels.Channels.GetByIdRequest;
import static tv.pluto.ip.dal.channels.Channels.GetByIdResponse;
import static tv.pluto.ip.dal.channels.Channels.GetByIdsRequest;
import static tv.pluto.ip.dal.channels.Channels.GetByIdsResponse;
import static tv.pluto.ip.dal.channels.Channels.GetCFaasIntegratedChannelProjection;
import static tv.pluto.ip.dal.channels.Channels.GetCFaasIntegratedChannelResponse;
import static tv.pluto.ip.dal.channels.Channels.GetChannelByFilterRequest;
import static tv.pluto.ip.dal.channels.Channels.GetChannelByFilterResponse;
import static tv.pluto.ip.dal.channels.Channels.GetChannelForActiveContentRequest;
import static tv.pluto.ip.dal.channels.Channels.GetChannelForActiveContentResponse;
import static tv.pluto.ip.dal.channels.Channels.GetChannelQueueRequest;
import static tv.pluto.ip.dal.channels.Channels.GetDocumentsUpdatedBetweenDatesRequest;
import static tv.pluto.ip.dal.channels.Channels.GetLibrarySumDurationRequest;
import static tv.pluto.ip.dal.channels.Channels.GetNonStitchedChannelsWithMultipleScheduledClipsParams;
import static tv.pluto.ip.dal.channels.Channels.GetNonStitchedChannelsWithMultipleScheduledClipsResponse;
import static tv.pluto.ip.dal.channels.Channels.GetQueueSumDurationRequest;
import static tv.pluto.ip.dal.channels.Channels.GetStitchedActiveChannelIdsRequest;
import static tv.pluto.ip.dal.channels.Channels.GetStitchedActiveChannelIdsResponse;
import static tv.pluto.ip.dal.channels.Channels.GetTimelinesForActiveContentRequest;
import static tv.pluto.ip.dal.channels.Channels.GetTimelinesForActiveContentResponse;
import static tv.pluto.ip.dal.channels.Channels.UpdateFieldsResponse;
import static tv.pluto.mongo.utils.MongoUtils.toMongoDate;

import io.grpc.Channel;
import java.util.ArrayList;
import java.util.Arrays;
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.assertj.core.util.Streams;
import tv.pluto.grpc.GenericGrpcClient;
import tv.pluto.ip.dal.channels.ChannelManagerGrpc;
import tv.pluto.mongo.entities.channels.Channels;

/** Class represents gRPC client for <b>channels.proto</b> file */
public class ChannelsClient extends GenericGrpcClient {

  private static final Logger logger = Logger.getLogger(ChannelsClient.class.getName());

  private ChannelManagerGrpc.ChannelManagerBlockingStub blockingStub;

  private tv.pluto.ip.dal.channels.Channels.GetAllPagedRequest.Builder getAllPagedBuilder =
      tv.pluto.ip.dal.channels.Channels.GetAllPagedRequest.newBuilder();

  public ChannelsClient(Channel channel, boolean auth) {
    blockingStub = ChannelManagerGrpc.newBlockingStub(channel);

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

    this.getDocumentsUpdatedBetweenDatesRequestBuilder =
        GetDocumentsUpdatedBetweenDatesRequest.newBuilder();
  }

  /**
   * Method represents gRPC method GetStitchedActiveChannelIds from <b>channels.proto</b> file
   *
   * @param activeRegion Active region
   * @return list of ids
   */
  public GetStitchedActiveChannelIdsResponse getStitchedActiveChannelIds(String activeRegion) {
    logger.info("Will try to get stitched active channel Ids for " + activeRegion + " ...");
    GetStitchedActiveChannelIdsRequest request =
        GetStitchedActiveChannelIdsRequest.newBuilder().setActiveRegion(activeRegion).build();

    return blockingStub.getStitchedActiveChannelIds(request);
  }

  /**
   * Method represents gRPC method GetChannelForActiveContent from <b>channels.proto</b> file
   *
   * @param id id to search
   * @return channel
   */
  public GetChannelForActiveContentResponse getChannelForActiveContent(String id) {
    logger.info("Will try to get channel for active content " + id + " ...");
    GetChannelForActiveContentRequest request =
        GetChannelForActiveContentRequest.newBuilder().setId(id).build();

    return blockingStub.getChannelForActiveContent(request);
  }

  /**
   * Method represents gRPC method GetTimelinesForActiveContent from <b>channels.proto</b> file
   *
   * @param activeContent activeContent object
   * @return timeline for active content
   */
  public GetTimelinesForActiveContentResponse getTimelinesForActiveContent(
      ActiveContent activeContent) {
    logger.info("Will try to get timelines for active content " + activeContent + " ...");
    GetTimelinesForActiveContentRequest.Builder requestBuilder =
        GetTimelinesForActiveContentRequest.newBuilder();

    if (activeContent.getFrom() != null) {
      requestBuilder.setFrom(toMongoDate(activeContent.getFrom()));
    }

    if (activeContent.getTo() != null) {
      requestBuilder.setTo(toMongoDate(activeContent.getTo()));
    }

    if (activeContent.getChannelId() != null) {
      requestBuilder.setChannel(activeContent.getChannelId());
    }

    if (activeContent.getSort() != null) {
      requestBuilder.setSort(activeContent.getSort());
    }

    if (activeContent.getLimit() != null) {
      requestBuilder.setLimit(activeContent.getLimit());
    }

    if (activeContent.getProjection() != null) {
      requestBuilder.addAllProjection(activeContent.getProjection());
    }

    GetTimelinesForActiveContentRequest request = requestBuilder.build();

    return blockingStub.getTimelinesForActiveContent(request);
  }

  /**
   * Method represents gRPC method GetChannelById from <b>channels.proto</b> file
   *
   * @param channelId id of channel to search
   * @return channel
   */
  public GetByIdResponse getChannelById(String channelId) {
    logger.info("Will try to get channel by id " + channelId + " ...");
    GetByIdRequest request = GetByIdRequest.newBuilder().setId(channelId).build();

    return blockingStub.getById(request);
  }

  public GetByIdResponse getChannelById(String channelId, String[] listProjections) {
    GetByIdRequest.Builder builder = GetByIdRequest.newBuilder();

    if (listProjections != null) {
      builder.addAllProjection(List.of(listProjections));
    }

    if (channelId != null) {
      builder.setId(channelId);
    }

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

  /**
   * Method represents gRPC method GetChannelsByIds from <b>channels.proto</b> file
   *
   * @param channelsIds List of channels ids to
   * @return list of channels
   */
  public GetByIdsResponse getChannelsByIds(List<String> channelsIds) {
    logger.info("Will try to get channels by ids " + channelsIds + " ...");

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

    return blockingStub.getByIds(request);
  }

  /**
   * Method represents gRPC method GetNonStitchedChannelsWithMultipleScheduledClips from
   * <b>channels.proto</b> file
   *
   * @return GetNonStitchedChannelsWithMultipleScheduledClipsResponse object
   */
  public GetNonStitchedChannelsWithMultipleScheduledClipsResponse
      getNonStitchedChannelsWithMultipleScheduledClips() {
    GetNonStitchedChannelsWithMultipleScheduledClipsParams request =
        GetNonStitchedChannelsWithMultipleScheduledClipsParams.newBuilder().build();

    return blockingStub.getNonStitchedChannelsWithMultipleScheduledClips(request);
  }

  public Iterator<tv.pluto.ip.dal.channels.Channels.Channel> getAllResponse(String activeRegion) {
    tv.pluto.ip.dal.channels.Channels.GetAllRequest channelsGetAllRequest =
        tv.pluto.ip.dal.channels.Channels.GetAllRequest.newBuilder()
            .setRegion(activeRegion)
            .build();
    return blockingStub.getAll(channelsGetAllRequest);
  }

  public List<tv.pluto.ip.dal.channels.Channels.Id> channelsIdsFromCFeeds() {
    tv.pluto.ip.dal.channels.Channels.CFeedsSyncAllIdsRequest cFeedsRequest =
        tv.pluto.ip.dal.channels.Channels.CFeedsSyncAllIdsRequest.newBuilder().build();
    Iterator<tv.pluto.ip.dal.channels.Channels.Id> channelsIds =
        blockingStub.cFeedsSyncAllIds(cFeedsRequest);
    return Streams.stream(channelsIds).collect(Collectors.toList());
  }

  public List<tv.pluto.ip.dal.channels.Channels.Channel> getCFeedsSyncDocumentsList(
      String lastRun, boolean isPublished) {
    CFeedsSyncDocumentsRequest request =
        CFeedsSyncDocumentsRequest.newBuilder()
            .setLastRunAt(lastRun)
            .setPublished(isPublished)
            .build();

    Iterator<tv.pluto.ip.dal.channels.Channels.Channel> cFeedsDocs =
        blockingStub.cFeedsSyncDocuments(request);
    return Streams.stream(cFeedsDocs).collect(Collectors.toList());
  }

  public List<tv.pluto.ip.dal.channels.Channels.Filter> filtersBodyInChannels(Channels channels) {
    List<String> listFilters =
        Stream.of("activeRegion", "kidsMode", "published", "plutoOfficeOnly", "name", "number")
            .collect(Collectors.toList());

    List<tv.pluto.ip.dal.channels.Channels.Filter> filtersInChannels = new ArrayList<>();

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

          switch (field) {
            case "activeRegion":
              value = channels.getActiveRegion();
              type = "string";
              break;
            case "kidsMode":
              value = channels.getKidsMode().toString();
              type = "boolean";
              break;
            case "published":
              value = channels.getPublished().toString();
              type = "boolean";
              break;
            case "plutoOfficeOnly":
              value = channels.getPlutoOfficeOnly().toString();
              type = "boolean";
              break;
            case "name":
              value = channels.getName();
              type = "string";
              break;
            case "number":
              value = channels.getNumber().toString();
              type = "number";
              break;
          }

          assert value != null;
          if (!value.equals("0")) {
            tv.pluto.ip.dal.channels.Channels.Filter channelNumberFilterBody =
                channelFilterBody(field, value, type);
            filtersInChannels.add(channelNumberFilterBody);
          }
        });

    return filtersInChannels;
  }

  public tv.pluto.ip.dal.channels.Channels.Filter channelFilterBody(
      String field, String value, String type) {

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

  public tv.pluto.ip.dal.channels.Channels.GetAllPagedSort sortObject(String name, int value) {
    return tv.pluto.ip.dal.channels.Channels.GetAllPagedSort.newBuilder()
        .setField(name)
        .setValue(value)
        .build();
  }

  public List<tv.pluto.ip.dal.channels.Channels.GetAllPagedSort> singleListSortObject(
      String name, int value) {
    return Collections.singletonList(sortObject(name, value));
  }

  public List<tv.pluto.ip.dal.channels.Channels.Filter> listFiltersBodyInChannels(
      tv.pluto.ip.dal.channels.Channels.Filter... filtersBodyInChannels) {
    return Arrays.asList(filtersBodyInChannels);
  }

  public UpdateFieldsResponse getUpdateFieldsResponse(
      String id, tv.pluto.ip.dal.channels.Channels.Channel channelsList, List<String> listFields) {
    tv.pluto.ip.dal.channels.Channels.UpdateFieldsRequest updateRequest =
        tv.pluto.ip.dal.channels.Channels.UpdateFieldsRequest.newBuilder()
            .setId(id)
            .setDocument(channelsList)
            .addAllUpdateFields(listFields)
            .build();

    return blockingStub.updateFields(updateRequest);
  }

  public CreateResponse create(tv.pluto.ip.dal.channels.Channels.Channel channel) {
    return blockingStub.create(CreateRequest.newBuilder().setDocument(channel).build());
  }

  public BulkDeviceUpdateResponse bulkDeviceUpdate(
      List<String> devices,
      List<String> includeGroup,
      List<String> excludeGroup,
      List<String> removeGroup) {
    BulkDeviceUpdateRequest.Builder builder = BulkDeviceUpdateRequest.newBuilder();

    if (devices != null) {
      builder.addAllDevices(devices);
    }

    if (includeGroup != null) {
      builder.addAllIncludeGroup(includeGroup);
    }

    if (excludeGroup != null) {
      builder.addAllExcludeGroup(excludeGroup);
    }

    if (removeGroup != null) {
      builder.addAllRemoveGroup(removeGroup);
    }

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

  public tv.pluto.ip.dal.channels.Channels.Filter getFilter(
      String field, String value, String type, String operator) {
    tv.pluto.ip.dal.channels.Channels.Filter.Builder builder =
        tv.pluto.ip.dal.channels.Channels.Filter.newBuilder()
            .setField(field)
            .setValue(value)
            .setType(type);

    if (type.isEmpty()) builder.setType("string");
    else builder.setType(type);

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

    return builder.build();
  }

  public GetAllPagedChannelLiteResponse getAllPagedChannelLite(
      Integer limit,
      Integer offset,
      List<GetAllPagedSort> grpcSort,
      List<Filter> listFilter,
      boolean isCount) {
    GetAllPagedChannelLiteRequest request =
        GetAllPagedChannelLiteRequest.newBuilder()
            .setLimit(limit)
            .setOffset(offset)
            .addAllSort(grpcSort)
            .addAllFilters(listFilter)
            .setDisableTotalCount(isCount)
            .build();

    return blockingStub.getAllPagedChannelLite(request);
  }

  public GetCFaasIntegratedChannelResponse getCfaasIntegratedChannel(
      tv.pluto.ip.dal.channels.Channels.GetCFaasIntegratedChannelRequest request) {
    return blockingStub.getCFaasIntegratedChannel(request);
  }

  public GetCFaasIntegratedChannelProjection getCFaasProjection(boolean isTrue) {
    tv.pluto.ip.dal.channels.Channels.SourceIdProjection.Builder sourceBuilder =
        GetCFaasIntegratedChannelProjection.newBuilder()
            .getCfaasIntegration()
            .getSourceId()
            .newBuilderForType()
            .setKey(isTrue)
            .setValue(isTrue);

    tv.pluto.ip.dal.channels.Channels.cfaasIntegrationProjection.Builder integrationBuilder =
        GetCFaasIntegratedChannelProjection.newBuilder()
            .getCfaasIntegrationBuilder()
            .setEnabled(isTrue)
            .setClipId(isTrue)
            .setCategory(isTrue)
            .setSubCategory(isTrue)
            .setGenre(isTrue)
            .setSubGenre(isTrue)
            .setSourceId(sourceBuilder);

    return GetCFaasIntegratedChannelProjection.newBuilder()
        .setName(isTrue)
        .setSummary(isTrue)
        .setDescription(isTrue)
        .setMetadataLanguage(isTrue)
        .setActiveRegion(isTrue)
        .setPublished(isTrue)
        .setTags(isTrue)
        .setAutoEpgSource(isTrue)
        .setCfaasIntegration(integrationBuilder)
        .build();
  }

  public tv.pluto.ip.dal.channels.Channels.GetChannelQueueResponse getChannelQueue(String id) {
    GetChannelQueueRequest request = GetChannelQueueRequest.newBuilder().setChannelId(id).build();
    return blockingStub.getChannelQueue(request);
  }

  public tv.pluto.ip.dal.channels.Channels.GetChannelQueueResponse getChannelQueueRequest(
      GetChannelQueueRequest request) {
    return blockingStub.getChannelQueue(request);
  }

  public tv.pluto.ip.dal.channels.Channels.BulkRemoveEpisodesFromQueueResponse
      bulkRemoveEpisodesFromQueue(List<String> episodeIds) {

    BulkRemoveEpisodesFromQueueRequest request =
        BulkRemoveEpisodesFromQueueRequest.newBuilder().addAllIds(episodeIds).build();

    return blockingStub.bulkRemoveEpisodesFromQueue(request);
  }

  public tv.pluto.ip.dal.channels.Channels.GetLibrarySumDurationResponse getLibrarySumDuration(
      GetLibrarySumDurationRequest request) {

    return blockingStub.getLibrarySumDuration(request);
  }

  public tv.pluto.ip.dal.channels.Channels.GetQueueSumDurationResponse getQueueSumDuration(
      GetQueueSumDurationRequest request) {

    return blockingStub.getQueueSumDuration(request);
  }

  public GetChannelByFilterResponse channelsByFilter(GetChannelByFilterRequest request) {

    return blockingStub.getChannelByFilter(request);
  }
}
