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

import static com.mongodb.client.model.Projections.include;
import static tv.pluto.ip.dal.channelcategory.ChannelCategories.ChannelCategory;

import io.grpc.StatusRuntimeException;
import io.qameta.allure.Description;
import io.qameta.allure.TmsLink;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import net.datafaker.Faker;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import tv.pluto.core.BaseTest;
import tv.pluto.grpc.channel_categories.ChannelCategoriesClient;
import tv.pluto.mongo.dto.ContentFeedQuery;
import tv.pluto.mongo.entities.channelcategories.ChannelCategories;
import tv.pluto.mongo.utils.ChannelCategoriesUtils;
import tv.pluto.utils.CommonUtils;

public class ChannelCategoriesTestSuite extends BaseTest {
  private ChannelCategoriesUtils channelCategoriesUtils;
  private ChannelCategoriesClient channelCategoriesClient;

  @BeforeClass(alwaysRun = true)
  private void setUp() {
    channelCategoriesUtils = new ChannelCategoriesUtils();
    channelCategoriesClient = new ChannelCategoriesClient(grpcChannelManager.getChannel());
  }

  @Test
  @Description(
      "GetDocumentsUpdatedBetweenDates - Time period for which there is a channel category")
  @TmsLink("28500425")
  public void C28500425_TEST() {

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

    List<ChannelCategories> mongoChannelCategories =
        channelCategoriesUtils.getDocumentsForContentFeeds(contentFeedQuery);
    List<ChannelCategories> grpcChannelCategories = new ArrayList<>();

    Iterator<ChannelCategory> categories =
        channelCategoriesClient.getDocumentsUpdatedBetweenDates(
            channelCategoriesUtils.updatedAtMinString, channelCategoriesUtils.updatedAtMaxString);

    while (categories.hasNext()) {
      grpcChannelCategories.add(
          channelCategoriesUtils.mapGrpcObjectToPojo(categories.next(), ChannelCategories.class));
    }

    mongoChannelCategories.sort(Comparator.comparing(ChannelCategories::getId));
    grpcChannelCategories.sort(Comparator.comparing(ChannelCategories::getId));

    List<String> mongoIds = mongoChannelCategories.stream().map(ChannelCategories::getId).toList();
    List<String> grpcIds = grpcChannelCategories.stream().map(ChannelCategories::getId).toList();

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

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = NOT_FOUND_MESSAGE)
  @Description("GetDocumentsUpdatedBetweenDates - Time period without channel category")
  @TmsLink("28500426")
  public void C28500426_TEST() {
    channelCategoriesClient
        .getDocumentsUpdatedBetweenDates(failStartDateTime, failEndDateTime)
        .next();
  }

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

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

  @Test
  @Description("CFeedsSyncAllIds - get CFEEDs ids")
  @TmsLink("28500437")
  public void C28500437_TEST() {
    List<String> channelCategoryIds =
        channelCategoriesClient.channelCategoryIdsFromCFeeds().stream()
            .map(cat -> cat.getId())
            .collect(Collectors.toList());

    List<String> grpcCategoriesByIds =
        channelCategoriesUtils.getDocumentsByIds(channelCategoryIds).stream()
            .map(channelCategory -> ((ChannelCategories) channelCategory).getId())
            .collect(Collectors.toList());

    Assert.assertTrue(CommonUtils.isTwoListsEqual(channelCategoryIds, grpcCategoriesByIds));
  }

  @Test
  @Description("cFeedsSyncDocuments - returns all channelCategories")
  @TmsLink("28500438")
  public void C28500438_TEST() {
    ChannelCategories randomDocument = channelCategoriesUtils.getRandomDocument();
    Iterator iteratorGrpcCFeedsSync =
        channelCategoriesClient.getCFeedsResponse(
            null, randomDocument.getUpdatedAt() + "Z", null, "cFeedsSyncDocuments");
    List grpcCFeedsSyncDocuments = CommonUtils.iteratorToList(iteratorGrpcCFeedsSync);
    List<ChannelCategories> convertedGrpcCFeeds =
        channelCategoriesUtils.mapGrpcObjectsToPojo(
            grpcCFeedsSyncDocuments, ChannelCategories.class);
    List<ChannelCategories> mongoCFeedsSyncDocuments =
        channelCategoriesUtils.getDocumentsForFeedsSyncStartTime(randomDocument.getUpdatedAt());

    Assert.assertTrue(
        channelCategoriesUtils.isTwoListEqual(convertedGrpcCFeeds, mongoCFeedsSyncDocuments));
  }

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = NOT_FOUND_MESSAGE)
  @Description("cFeedsSyncDocuments - does not return any channelCategories for the future time")
  @TmsLink("28500439")
  public void C28500439_TEST() {
    String time = CommonUtils.localDateTimeStringPlusDays(1);
    channelCategoriesClient.getCFeedsResponse(null, time, null, "cFeedsSyncDocuments").next();
  }

  @Test
  @Description("GetById - verify the object with valid id")
  @TmsLink("28500446")
  public void C28500446_TEST() {
    ChannelCategories randomDocumenDb = channelCategoriesUtils.getRandomDocument();
    ChannelCategory channelCategoryGrpc =
        channelCategoriesClient.getById(randomDocumenDb.getId()).getDocument();
    ChannelCategories channelCategoriesGrpc =
        channelCategoriesUtils.mapGrpcObjectToPojo(channelCategoryGrpc, ChannelCategories.class);

    Assert.assertNotNull(randomDocumenDb);
    Assert.assertEquals(randomDocumenDb, channelCategoriesGrpc);
  }

  @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "INTERNAL.*")
  @Description("GetById - no id")
  @TmsLink("28500447")
  public void C28500447_TEST() {
    channelCategoriesClient.getById("");
  }

  @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = NOT_FOUND)
  @Description("GetById - invalid id")
  @TmsLink("28500448")
  public void C28500448_TEST() {
    channelCategoriesClient.getById(INVALID_ID);
  }

  @Test
  @Description("UpdateFields - verify the object with valid id")
  @TmsLink("28500449")
  public void C28500449_TEST() {
    ChannelCategories randomDocumenDb = channelCategoriesUtils.getRandomDocument();
    List<String> updatedFields = List.of("name", "order");
    ChannelCategory chanCategoryGrpc =
        channelCategoriesClient.getById(randomDocumenDb.getId()).getDocument();

    // name should start from upper case. In schema
    String name = new Faker().name().firstName();
    ChannelCategory chanCatGrpc = chanCategoryGrpc.toBuilder().setName(name).setOrder(1).build();

    ChannelCategory updatedFieldsGrpc =
        channelCategoriesClient
            .updateFieldsRequest(randomDocumenDb.getId(), chanCatGrpc, updatedFields)
            .getDocument();
    Assert.assertEquals(chanCatGrpc.getId(), updatedFieldsGrpc.getId());
    Assert.assertEquals(chanCatGrpc.getName(), updatedFieldsGrpc.getName());
    Assert.assertEquals(chanCatGrpc.getOrder(), updatedFieldsGrpc.getOrder());
    Assert.assertEquals(chanCatGrpc.getCreatedAt(), updatedFieldsGrpc.getCreatedAt());
    Assert.assertEquals(chanCatGrpc.getPngImageUrl(), updatedFieldsGrpc.getPngImageUrl());
    Assert.assertEquals(chanCatGrpc.getSvgImageUrl(), updatedFieldsGrpc.getSvgImageUrl());

    // revert back
    channelCategoriesClient.updateFieldsRequest(
        randomDocumenDb.getId(), chanCategoryGrpc, updatedFields);
  }

  @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "INTERNAL.*")
  @Description("UpdateFields - id is incorrect")
  @TmsLink("28500450")
  public void C28500450_TEST() {
    channelCategoriesClient.updateFieldsRequest("test", null, new ArrayList<>());
  }

  @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = NOT_FOUND)
  @Description("UpdateFields - invalid id")
  @TmsLink("28500451")
  public void C28500451_TEST() {
    channelCategoriesClient.updateFieldsRequest(INVALID_ID, null, new ArrayList<>());
  }
}
