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

import static com.mongodb.client.model.Projections.include;
import static tv.pluto.ip.dal.rating.Ratings.Filter;
import static tv.pluto.ip.dal.rating.Ratings.GetAllPagedSort;
import static tv.pluto.ip.dal.rating.Ratings.Rating;

import io.grpc.StatusRuntimeException;
import io.qameta.allure.Description;
import io.qameta.allure.TmsLink;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import tv.pluto.core.BaseTest;
import tv.pluto.grpc.ratings.RatingsClient;
import tv.pluto.mongo.dto.ContentFeedQuery;
import tv.pluto.mongo.entities.ratings.Ratings;
import tv.pluto.mongo.utils.RatingsUtils;
import tv.pluto.utils.CommonUtils;

public class RatingsTestSuite extends BaseTest {
  private RatingsClient ratingsClient;
  private RatingsUtils ratingsUtils;

  @BeforeClass(alwaysRun = true)
  private void setUo() {
    ratingsClient = new RatingsClient(grpcChannelManager.getChannel());
    ratingsUtils = new RatingsUtils();
  }

  @Test
  @Description("GetDocumentsUpdatedBetweenDates - Time period for which there is a rating")
  @TmsLink("28501258")
  public void C28501258_TEST() {

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

    List<Ratings> mongoRatings = ratingsUtils.getDocumentsForContentFeeds(contentFeedQuery);
    List<Ratings> grpcRatings = new ArrayList<>();

    Iterator<Rating> ratings =
        ratingsClient.getDocumentsUpdatedBetweenDates(
            ratingsUtils.updatedAtMinString, ratingsUtils.updatedAtMaxString);

    while (ratings.hasNext()) {
      grpcRatings.add(ratingsUtils.mapGrpcObjectToPojo(ratings.next(), Ratings.class));
    }

    mongoRatings.sort(Comparator.comparing(Ratings::getId));
    grpcRatings.sort(Comparator.comparing(Ratings::getId));

    List<String> mongoIds = mongoRatings.stream().map(Ratings::getId).toList();
    List<String> grpcIds = grpcRatings.stream().map(Ratings::getId).toList();

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

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = NOT_FOUND_MESSAGE)
  @Description("GetDocumentsUpdatedBetweenDates - Time period without rating")
  @TmsLink("28501259")
  public void C28501259_TEST() {
    ratingsClient.getDocumentsUpdatedBetweenDates(failStartDateTime, failEndDateTime).next();
  }

  @Test(
      dataProvider = "fail-content-feeds-test-data",
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = INVALID_ARGUMENT_MESSAGE)
  @Description(
      "GetDocumentsUpdatedBetweenDates - Invalid values for start_date_time and end_date_time")
  @TmsLink("28501260")
  public void C28501260_TEST(String startDateTime, String endDateTime) {
    ratingsClient.getDocumentsUpdatedBetweenDates(startDateTime, endDateTime).next();
  }

  @Test(
      dataProvider = "less-or-equal-fail-content-feeds-test-data",
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = INTERNAL_ERROR_MESSAGE)
  @Description(
      "GetDocumentsUpdatedBetweenDates - end_date_time is less than or equal to start_date_time")
  @TmsLink("28501261")
  public void C28501261_TEST(String startDateTime, String endDateTime) {
    ratingsClient.getDocumentsUpdatedBetweenDates(startDateTime, endDateTime).next();
  }

  @Test
  @Description("CFeedsSyncAllIds - stream returns CFEEDS stream of Ratings ids")
  @TmsLink("28501270")
  public void C28501270_TEST() {
    Iterator<?> cFeedsSyncAllIds =
        new RatingsClient(grpcChannelManager.getChannel())
            .getCFeedsResponse(null, null, null, "cFeedsSyncAllIds");
    List<String> listCFeedsSyncAllIds =
        CommonUtils.iteratorToList(cFeedsSyncAllIds).stream()
            .map(o -> ((tv.pluto.ip.dal.rating.Ratings.Id) o).getId())
            .sorted()
            .collect(Collectors.toList());
    List<String> mongoIds =
        ratingsUtils.getListOfAllObjects().stream()
            .map(o -> ((Ratings) o).getId())
            .sorted()
            .collect(Collectors.toList());

    Assert.assertTrue(listCFeedsSyncAllIds.size() > 0);
    Assert.assertTrue(CommonUtils.isTwoListsEqual(listCFeedsSyncAllIds, mongoIds));
  }

  @Test
  @Description("cFeedsSyncDocuments - last_run_at")
  @TmsLink("28501271")
  public void C28501271_TEST() {
    Ratings partners = ratingsUtils.getRandomDocument();
    Iterator<?> cFeedsSyncAllIds =
        new RatingsClient(grpcChannelManager.getChannel())
            .getCFeedsResponse(null, partners.getUpdatedAt() + "Z", null, "cFeedsSyncDocuments");
    List<?> listPartners = CommonUtils.iteratorToList(cFeedsSyncAllIds);
    List<Ratings> grpcMainCategories =
        ratingsUtils.mapGrpcObjectsToPojo(listPartners, Ratings.class);

    List<Ratings> mongoListCategories =
        ratingsUtils.getDocumentsForFeedsSyncStartTime(partners.getUpdatedAt()).stream()
            .map(o -> ((Ratings) o))
            .collect(Collectors.toList());

    grpcMainCategories.sort(Comparator.comparing(Ratings::getId));
    mongoListCategories.sort(Comparator.comparing(Ratings::getId));

    List<String> listIdsPartners = grpcMainCategories.stream().map(Ratings::getId).toList();
    List<String> listIdsMongoPartners = mongoListCategories.stream().map(Ratings::getId).toList();

    Assert.assertFalse(grpcMainCategories.isEmpty());
    Assert.assertEquals(listIdsPartners, listIdsMongoPartners);
  }

  @Test(
      expectedExceptions = RuntimeException.class,
      expectedExceptionsMessageRegExp = NOT_FOUND_MESSAGE)
  @Description("cFeedsSyncDocuments - last_run_at future time")
  @TmsLink("28501272")
  public void C28501272_TEST() {
    String futureTime = CommonUtils.localDateTimeStringPlusDays(10);
    ratingsClient.getCFeedsResponse(null, futureTime, null, "cFeedsSyncDocuments").next();
  }

  @Test
  @Description("GetAllPaged - filter by id")
  @TmsLink("28501273")
  public void C28501273_TEST() {
    int limit = 10;
    int offset = 0;

    Ratings ratingsMongo = ratingsUtils.getRandomDocument();
    GetAllPagedSort allSort = GetAllPagedSort.newBuilder().setValue(1).setField("_id").build();
    List<GetAllPagedSort> listGetAllPagedSort = List.of(allSort);

    List<Filter> listFilter =
        Collections.singletonList(
            Filter.newBuilder()
                .setField("_id")
                .setValue(ratingsMongo.getId())
                .setType("string")
                .build());

    tv.pluto.ip.dal.rating.Ratings.GetAllPagedResponse allPaged =
        new RatingsClient(grpcChannelManager.getChannel())
            .getAllPaged(limit, offset, listGetAllPagedSort, listFilter, true);
    Assert.assertEquals(1, allPaged.getDocumentsList().size());
    Assert.assertEquals(offset, allPaged.getMetadata().getCurrentOffset());
    Assert.assertEquals(1, allPaged.getMetadata().getTotalItems());
    Assert.assertEquals(ratingsMongo.getId(), allPaged.getDocumentsList().get(0).getId());

    Rating rating = allPaged.getDocumentsList().get(0);
    Ratings gRatings = ratingsUtils.mapGrpcObjectToPojo(rating, Ratings.class);

    Assert.assertEquals(gRatings, ratingsMongo);
  }

  @Test
  @Description("GetAllPaged - filter by region")
  @TmsLink("28501274")
  public void C28501274_TEST() {
    int limit = 0;

    Ratings ratingsMongo = ratingsUtils.getRandomDocument();
    List<Ratings> listRating =
        ratingsUtils.getDocumentsByFieldAndValue("region", ratingsMongo.getRegion());

    GetAllPagedSort allSort = GetAllPagedSort.newBuilder().setValue(1).setField("_id").build();
    List<GetAllPagedSort> listGetAllPagedSort = List.of(allSort);

    List<Filter> listFilter =
        Collections.singletonList(
            Filter.newBuilder()
                .setField("region")
                .setValue(ratingsMongo.getRegion())
                .setType("string")
                .build());

    tv.pluto.ip.dal.rating.Ratings.GetAllPagedResponse allPaged =
        new RatingsClient(grpcChannelManager.getChannel())
            .getAllPaged(limit, limit, listGetAllPagedSort, listFilter, true);

    Assert.assertEquals(listRating.size(), allPaged.getMetadata().getTotalItems());
    Assert.assertEquals(listRating.size(), allPaged.getDocumentsList().size());

    List<Ratings> listGrpcRatings =
        ratingsUtils.mapGrpcObjectsToPojo(allPaged.getDocumentsList(), Ratings.class);

    listRating.sort(Comparator.comparing(Ratings::getId));
    listGrpcRatings.sort(Comparator.comparing(Ratings::getId));

    Assert.assertEquals(listGrpcRatings, listRating);
  }

  @Test
  @Description("GetAllPaged - get all Ratings")
  @TmsLink("28501275")
  public void C28501275_TEST() {
    int limit = 0;
    List<Ratings> listRating = ratingsUtils.getDocuments(99999);

    tv.pluto.ip.dal.rating.Ratings.GetAllPagedResponse allPaged =
        new RatingsClient(grpcChannelManager.getChannel())
            .getAllPaged(limit, limit, null, null, true);

    Assert.assertEquals(listRating.size(), allPaged.getMetadata().getTotalItems());
    Assert.assertEquals(listRating.size(), allPaged.getDocumentsList().size());

    List<Ratings> listGrpcRatings =
        ratingsUtils.mapGrpcObjectsToPojo(allPaged.getDocumentsList(), Ratings.class);

    listRating.sort(Comparator.comparing(Ratings::getId));
    listGrpcRatings.sort(Comparator.comparing(Ratings::getId));

    Assert.assertEquals(listGrpcRatings, listRating);
  }

  @Test
  @Description("GetAllPaged - get limited all Ratings")
  @TmsLink("28501276")
  public void C28501276_TEST() {
    int limit = 10;
    tv.pluto.ip.dal.rating.Ratings.GetAllPagedResponse allPaged =
        new RatingsClient(grpcChannelManager.getChannel()).getAllPaged(limit, 0, null, null, true);

    Assert.assertEquals(limit, allPaged.getMetadata().getTotalItems());
    Assert.assertEquals(limit, allPaged.getDocumentsList().size());
  }

  @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = NOT_FOUND)
  @Description("GetAllPaged - get invalid rating")
  @TmsLink("28501277")
  public void C28501277_TEST()
      throws NoSuchFieldException, InvocationTargetException, IllegalAccessException {
    List<Filter> listFilter =
        Collections.singletonList(
            Filter.newBuilder().setField("region").setValue("testDAL").setType("string").build());

    Assert.assertNull(
        new RatingsClient(grpcChannelManager.getChannel())
            .getAllPagedException(1, 0, null, listFilter, true));
  }
}
