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

import static com.mongodb.client.model.Projections.include;
import static tv.pluto.ip.dal.activeregion.ActiveRegions.ActiveRegion;

import io.grpc.ManagedChannel;
import io.grpc.StatusRuntimeException;
import io.qameta.allure.Description;
import io.qameta.allure.TmsLink;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import org.assertj.core.util.Streams;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import tv.pluto.core.BaseTest;
import tv.pluto.grpc.active_regions.ActiveRegionsClient;
import tv.pluto.mongo.dto.ContentFeedQuery;
import tv.pluto.mongo.entities.activeregions.ActiveRegions;
import tv.pluto.mongo.utils.ActiveRegionsUtils;
import tv.pluto.utils.CommonUtils;

public class ActiveRegionsTestSuite extends BaseTest {
  private ActiveRegionsClient activeRegionsClient;
  private ActiveRegionsUtils activeRegionsUtils;

  @BeforeClass(alwaysRun = true)
  private void setUp() {
    ManagedChannel channel = grpcChannelManager.getChannel();
    activeRegionsClient = new ActiveRegionsClient(channel);
    activeRegionsUtils = new ActiveRegionsUtils();
  }

  @Test
  @Description("GetDocumentsUpdatedBetweenDates - Time period for which there is a region")
  @TmsLink("28500312")
  public void C28500312_TEST() {

    ContentFeedQuery contentFeedQuery =
        ContentFeedQuery.builder()
            .updatedAtMin(activeRegionsUtils.updatedAtMin)
            .updatedAtMax(activeRegionsUtils.updatedAtMax)
            .limit(0)
            .projections(include("id"))
            .build();

    List<ActiveRegions> mongoActiveRegions =
        activeRegionsUtils.getDocumentsForContentFeeds(contentFeedQuery);
    List<ActiveRegions> grpcActiveRegions = new ArrayList<>();

    Iterator<ActiveRegion> regions =
        activeRegionsClient.getDocumentsUpdatedBetweenDates(
            activeRegionsUtils.updatedAtMinString, activeRegionsUtils.updatedAtMaxString);

    while (regions.hasNext()) {
      grpcActiveRegions.add(
          activeRegionsUtils.mapGrpcObjectToPojo(regions.next(), ActiveRegions.class));
    }

    mongoActiveRegions.sort(Comparator.comparing(ActiveRegions::getId));
    grpcActiveRegions.sort(Comparator.comparing(ActiveRegions::getId));

    List<String> mongoIds = mongoActiveRegions.stream().map(ActiveRegions::getId).toList();
    List<String> grpcIds = grpcActiveRegions.stream().map(ActiveRegions::getId).toList();

    Assert.assertEquals(
        mongoIds, grpcIds, "The IDs do not match between Mongo and gRPC active regions.");
  }

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = NOT_FOUND_MESSAGE)
  @Description("GetDocumentsUpdatedBetweenDates - Time period without a region")
  @TmsLink("28500313")
  public void C28500313_TEST() {
    activeRegionsClient.getDocumentsUpdatedBetweenDates(failStartDateTime, failEndDateTime).next();
  }

  @Test(
      dataProvider = "fail-content-feeds-test-data",
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = INVALID_ARGUMENT_MESSAGE)
  @TmsLink("28500314")
  @Description(
      "GetDocumentsUpdatedBetweenDates - Invalid values for start_date_time and end_date_time")
  public void C28500314_TEST(String updatedAtMin, String updatedAtMax) {
    activeRegionsClient.getDocumentsUpdatedBetweenDates(updatedAtMin, updatedAtMax).next();
  }

  @Test(
      dataProvider = "less-or-equal-fail-content-feeds-test-data",
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = INTERNAL_ERROR_MESSAGE)
  @TmsLink("28500315")
  @Description(
      "GetDocumentsUpdatedBetweenDates - end_date_time is less than or equal to start_date_time")
  public void C28500315_TEST(String updatedAtMin, String updatedAtMax) {
    activeRegionsClient.getDocumentsUpdatedBetweenDates(updatedAtMin, updatedAtMax).next();
  }

  @Test
  @Description("CFeedsSyncAllIds - get CFEEDs ids")
  @TmsLink("28500324")
  public void C28500324_TEST() {
    List<String> getActiveRegions =
        activeRegionsUtils.getActiveRegions(50).stream()
            .map(ActiveRegions::getId)
            .collect(Collectors.toList());

    Iterator<tv.pluto.ip.dal.activeregion.ActiveRegions.Id> idIterator =
        activeRegionsClient.cFeedsSyncAllIdsIterator();
    List<String> listGrpcIds =
        Streams.stream(idIterator)
            .limit(getActiveRegions.size())
            .map(tv.pluto.ip.dal.activeregion.ActiveRegions.Id::getId)
            .collect(Collectors.toList());

    Assert.assertTrue(CommonUtils.isTwoListsEqual(getActiveRegions, listGrpcIds));
  }

  @Test
  @Description("cFeedsSyncDocuments - last_run_at")
  @TmsLink("28500325")
  public void C28500325_TEST() {
    ActiveRegions randomActiveRegions = activeRegionsUtils.getActiveRegions(1).get(0);
    List<ActiveRegions> mongoActiveRegions =
        activeRegionsUtils.getAllActiveRegionsWithingUpdatedDate(
            randomActiveRegions.getUpdatedAt());

    List<ActiveRegions> grpcListActiveRegions = new ArrayList<>();
    activeRegionsClient
        .cFeedsSyncSyncDocumentsList(randomActiveRegions.getUpdatedAtString())
        .forEach(
            activeRegion ->
                grpcListActiveRegions.add(
                    activeRegionsUtils.mapGrpcObjectToPojo(activeRegion, ActiveRegions.class)));

    mongoActiveRegions.sort(Comparator.comparing(ActiveRegions::getId));
    grpcListActiveRegions.sort(Comparator.comparing(ActiveRegions::getId));

    Assert.assertFalse(grpcListActiveRegions.isEmpty());
    Assert.assertEquals(mongoActiveRegions, grpcListActiveRegions);
  }

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = NOT_FOUND_MESSAGE)
  @Description("cFeedsSyncDocuments -invalid last_run_at now time.")
  @TmsLink("28500326")
  public void C28500326_TEST() {
    LocalDateTime nowTime = LocalDateTime.now(ZoneOffset.UTC);
    int mongoActiveRegions =
        activeRegionsUtils.getAllActiveRegionsWithingUpdatedDate(nowTime).size();
    Assert.assertEquals(0, mongoActiveRegions);

    activeRegionsClient.cFeedsSyncSyncDocumentsList(nowTime.toString()).get(0);
  }

  @Test
  @Description("GetByCode - get active region by code")
  @TmsLink("28500327")
  public void C28500327_TEST() {

    tv.pluto.ip.dal.activeregion.ActiveRegions.GetByCodeResponse grpcActiveRegions =
        activeRegionsClient.getByCode("US");

    Assert.assertEquals(grpcActiveRegions.getDocument().getCode(), "US");
  }

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = NOT_FOUND)
  @Description("GetByCode - invalid country code")
  @TmsLink("28500328")
  public void C28500328_TEST() {
    activeRegionsClient.getByCode("USA").getDocument();
  }
}
