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

import static com.mongodb.client.model.Projections.include;
import static com.mongodb.client.model.Sorts.ascending;
import static com.mongodb.client.model.Sorts.descending;
import static com.mongodb.client.model.Sorts.orderBy;
import static tv.pluto.ip.dal.partner.Partners.Partner;

import com.mongodb.client.model.Collation;
import com.plutotv.test.lib.LogUtils;
import com.plutotv.test.lib.ttfb.grpc.GrpcCall;
import com.plutotv.test.lib.ttfb.grpc.TTFBGrpcValidator;
import io.grpc.StatusRuntimeException;
import io.qameta.allure.Description;
import io.qameta.allure.TmsLink;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
import net.datafaker.Faker;
import org.bson.conversions.Bson;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import tv.pluto.core.BaseTest;
import tv.pluto.grpc.partners.PartnersClient;
import tv.pluto.ip.dal.partner.Partners.GetByFilterResponse;
import tv.pluto.mongo.dto.ContentFeedQuery;
import tv.pluto.mongo.dto.DocumentQuery;
import tv.pluto.mongo.entities.common.AdvertisingId;
import tv.pluto.mongo.entities.common.ExternalIds;
import tv.pluto.mongo.entities.episodes.Episodes;
import tv.pluto.mongo.entities.partners.Partners;
import tv.pluto.mongo.entities.series.Series;
import tv.pluto.mongo.utils.MongoUtils;
import tv.pluto.mongo.utils.PartnersUtils;
import tv.pluto.mongo.utils.SeriesUtils;
import tv.pluto.mongo.utils.episodes.EpisodeUtils;
import tv.pluto.utils.CommonUtils;

public class PartnersTestSuite extends BaseTest {
  private PartnersClient partnersClient;
  private PartnersClient partnersClientAuth;
  private PartnersUtils partnersUtils;
  private SeriesUtils seriesUtils;
  private EpisodeUtils episodeUtils;

  @BeforeClass(alwaysRun = true)
  private void setUp() {
    partnersClient = new PartnersClient(grpcChannelManager.getChannel(), false);
    partnersClientAuth = new PartnersClient(grpcChannelManager.getChannel(), true);
    partnersUtils = new PartnersUtils();
    seriesUtils = new SeriesUtils();
    episodeUtils = new EpisodeUtils();
  }

  @Test
  @Description("Valid request - GetEpisodeById")
  @TmsLink("28501136")
  public void C28501136_TEST() {
    Partners mongoPartner = partnersUtils.getRandomDocument();

    String randomPartnerId = mongoPartner.getId();

    Partners mappedPartner =
        partnersUtils.mapGrpcObjectToPojo(
            partnersClient.getPartnerById(randomPartnerId).getDocument(), Partners.class);

    Assert.assertEquals(mongoPartner, mappedPartner);
  }

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = "NOT_FOUND: Partner document for requested ID not found")
  @Description("Invalid request - GetPartnerById")
  @TmsLink("28501137")
  public void C28501137_TEST() {
    partnersClient.getPartnerById(MongoUtils.INVALID_ID_VALUE);
  }

  @Test
  @Description("Valid request with single partner - GetPartnersByIds")
  @TmsLink("28501138")
  public void C28501138_TEST() {
    Partners mongoPartner = partnersUtils.getRandomDocument();
    List<String> randomPartnersIds = Collections.singletonList(mongoPartner.getId());

    List<Partners> mongoPartnersList = partnersUtils.getDocumentsByIds(randomPartnersIds);
    List<?> mappedPartnersList =
        partnersUtils.mapGrpcObjectsToPojo(
            partnersClient.getPartnersByIds(randomPartnersIds).getDocumentsList(), Partners.class);

    Assert.assertEquals(mongoPartnersList, mappedPartnersList);
  }

  @Test
  @Description("Valid request with multiple partners - GetPartnersByIds")
  @TmsLink("28501139")
  public void C28501139_TEST() {
    List<String> randomPartnersIds = partnersUtils.getRandomPartnerIds();

    List<Partners> mongoPartnersList = partnersUtils.getDocumentsByIds(randomPartnersIds);
    List<Partners> mappedPartnersList =
        partnersUtils.mapGrpcObjectsToPojo(
            partnersClient.getPartnersByIds(randomPartnersIds).getDocumentsList(), Partners.class);

    mappedPartnersList.sort(Comparator.comparing(Partners::getId));
    mongoPartnersList.sort(Comparator.comparing(Partners::getId));

    Assert.assertEquals(mongoPartnersList, mappedPartnersList);
  }

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = "NOT_FOUND: Partner documents for requested IDs not found")
  @Description("Invalid request with single partner - GetPartnersByIds")
  @TmsLink("28501140")
  public void C28501140_TEST() {
    List<String> randomPartnersIds = Collections.singletonList(MongoUtils.INVALID_ID_VALUE);
    Assert.assertTrue(
        partnersClient.getPartnersByIds(randomPartnersIds).getDocumentsList().isEmpty());
  }

  @Test
  @Description("Valid request with invalid partners - GetPartnersByIds")
  @TmsLink("28501141")
  public void C28501141_TEST() {
    List<String> randomPartnersIds = partnersUtils.getRandomPartnerIds();
    randomPartnersIds.add(MongoUtils.INVALID_ID_VALUE);

    List<Partners> mongoPartnersList = partnersUtils.getDocumentsByIds(randomPartnersIds);
    List<Partners> mappedPartnersList =
        partnersUtils.mapGrpcObjectsToPojo(
            partnersClient.getPartnersByIds(randomPartnersIds).getDocumentsList(), Partners.class);

    mappedPartnersList.sort(Comparator.comparing(Partners::getId));
    mongoPartnersList.sort(Comparator.comparing(Partners::getId));

    Assert.assertEquals(mongoPartnersList, mappedPartnersList);
  }

  @Test
  @Description("GetDocumentsUpdatedBetweenDates - Time period for which there is a partner")
  @TmsLink("28501155")
  public void C28501155_TEST() {

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

    List<Partners> mongoPartners = partnersUtils.getDocumentsForContentFeeds(contentFeedQuery);
    List<Partners> grpcPartners = new ArrayList<>();

    Iterator<Partner> partners =
        partnersClient.getDocumentsUpdatedBetweenDates(
            partnersUtils.updatedAtMinString, partnersUtils.updatedAtMaxString);

    while (partners.hasNext()) {
      grpcPartners.add(partnersUtils.mapGrpcObjectToPojo(partners.next(), Partners.class));
    }

    mongoPartners.sort(Comparator.comparing(Partners::getId));
    grpcPartners.sort(Comparator.comparing(Partners::getId));

    List<String> mongoIds = mongoPartners.stream().map(Partners::getId).toList();
    List<String> grpcIds = grpcPartners.stream().map(Partners::getId).toList();

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

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = NOT_FOUND_MESSAGE)
  @Description("GetDocumentsUpdatedBetweenDates - Time period without partners")
  @TmsLink("28501156")
  public void C28501156_TEST() {
    partnersClient.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("28501157")
  public void C28501157_TEST(String startDateTime, String endDateTime) {
    partnersClient.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("28501158")
  public void C28501158_TEST(String startDateTime, String endDateTime) {
    partnersClient.getDocumentsUpdatedBetweenDates(startDateTime, endDateTime).next();
  }

  @Test
  @Description("CFeedsSyncAllIds - stream returns CFEEDS stream of Partners ids")
  @TmsLink("28501167")
  public void C28501167_TEST() {
    Iterator<Object> cFeedsSyncAllIds =
        partnersClient.getCFeedsResponse(null, null, null, "cFeedsSyncAllIds");
    List<String> listCFeedsSyncAllIds =
        CommonUtils.iteratorToList(cFeedsSyncAllIds).stream()
            .map(o -> ((tv.pluto.ip.dal.partner.Partners.Id) o).getId())
            .sorted()
            .collect(Collectors.toList());
    List<String> mongoIds =
        partnersUtils.getListOfAllObjects().stream()
            .map(o -> ((Partners) o).getId())
            .sorted()
            .collect(Collectors.toList());

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

  @Test
  @Description("cFeedsSyncDocuments - last_run_at")
  @TmsLink("28501168")
  public void C28501168_TEST() {
    Partners partners = partnersUtils.getRandomDocument();
    Iterator<Object> cFeedsSyncAllIds =
        partnersClient.getCFeedsResponse(
            null, partners.getUpdatedAt() + "Z", null, "cFeedsSyncDocuments");
    List<?> listPartners = CommonUtils.iteratorToList(cFeedsSyncAllIds);
    List<Partners> grpcMainCategories =
        partnersUtils.mapGrpcObjectsToPojo(listPartners, Partners.class);

    List<Partners> mongoListCategories =
        partnersUtils.getDocumentsForFeedsSyncStartTime(partners.getUpdatedAt()).stream()
            .map(o -> ((Partners) o))
            .filter(o -> !o.getName().isEmpty())
            .collect(Collectors.toList());

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

    Assert.assertTrue(grpcMainCategories.size() > 0);
    Assert.assertEquals(grpcMainCategories, mongoListCategories);
  }

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

  @Test
  @Description("GetItemsAll - valid partner id")
  @TmsLink("28501170")
  public void C28501170_TEST() {
    Partners partner = partnersUtils.getRandomDocument();
    //        Partners partner = partnersUtils.getDocumentById("55fc943905dcc927a99b2658");
    String partnerId;
    if (partner == null) {
      LogUtils.logSevereMessage("Test partner is not valid anymore and test can run much longer");
      List<Series> listAllSeries =
          seriesUtils.getDocumentsByKeyValue("customReferences.type", "deliveryPartner");
      partnerId = listAllSeries.get(0).getCustomReferences().get(0).getValue();
    } else partnerId = partner.getId();

    List<Series> listSeries =
        seriesUtils.getDocumentsByKeyObjectIdValue("customReferences.value", partnerId);
    List<Episodes> listEpisodes =
        episodeUtils.getDocumentsByKeyObjectIdValue("customReferences.value", partnerId);

    List<tv.pluto.ip.dal.partner.Partners.PartnerContentItem> itemsList =
        partnersClient.getItemsAll(partnerId).getItemsList();
    int totalSeriesEpisodePartnersSize = listSeries.size() + listEpisodes.size();
    Assert.assertEquals(itemsList.size(), totalSeriesEpisodePartnersSize);

    List<tv.pluto.ip.dal.partner.Partners.PartnerContentItem> responseSeries =
        itemsList.stream()
            .filter(p -> p.getContentType().equals("series"))
            .collect(Collectors.toList());

    List<tv.pluto.ip.dal.partner.Partners.PartnerContentItem> responseEpisodes =
        itemsList.stream()
            .filter(p -> p.getContentType().equals("episode"))
            .collect(Collectors.toList());

    LogUtils.logInfoMessage("Total partners in the response: " + itemsList.size());
    Assert.assertEquals(responseSeries.size(), listSeries.size());
    Assert.assertEquals(responseEpisodes.size(), listEpisodes.size());

    if (responseSeries.size() != 0) {
      responseSeries.parallelStream()
          .forEach(
              partnerSeries -> {
                Series series =
                    listSeries.stream()
                        .filter(p -> p.getId().equals(partnerSeries.getSeriesId()))
                        .findFirst()
                        .get();
                Assert.assertEquals(series.getId(), partnerSeries.getSeriesId());
                Assert.assertEquals(series.getSlug(), partnerSeries.getSlug());

                List<String> listExternalIds =
                    series.getExternalIds().stream()
                        .map(ExternalIds::getId)
                        .collect(Collectors.toList());
                Assert.assertEquals(listExternalIds, partnerSeries.getExternalIdsList());
              });
    }

    if (responseEpisodes.size() != 0) {
      LogUtils.logInfoMessage("responseEpisodes: " + responseEpisodes.size());
      responseEpisodes.parallelStream()
          .forEach(
              partnerEpisodes -> {
                Episodes episode =
                    listEpisodes.stream()
                        .filter(p -> p.getId().equals(partnerEpisodes.getId()))
                        .findAny()
                        .orElse(null);
                Assert.assertNotNull(episode);

                Assert.assertEquals(episode.getId(), partnerEpisodes.getId());
                Assert.assertEquals(episode.getSlug(), partnerEpisodes.getSlug());
                Assert.assertEquals(episode.getSeries(), partnerEpisodes.getSeriesId());

                try {
                  Series seriesInEpisodes =
                      listSeries.stream()
                          .filter(s -> s.getId().equals(partnerEpisodes.getSeriesId()))
                          .findFirst()
                          .get();
                  Assert.assertEquals(seriesInEpisodes.getSlug(), partnerEpisodes.getSeriesSlug());
                  List<String> listExternalIds =
                      seriesInEpisodes.getExternalIds().stream()
                          .map(ExternalIds::getId)
                          .collect(Collectors.toList());

                  // To do convert different formats to compare
                  Assert.assertEquals(listExternalIds, partnerEpisodes.getExternalIdsList());
                } catch (NoSuchElementException e) {
                  LogUtils.logSevereMessage(
                      "No series for episode id: " + partnerEpisodes.getSeriesId());
                  Series seriesInEpisodes =
                      seriesUtils.getDocumentById(partnerEpisodes.getSeriesId());
                  Assert.assertEquals(seriesInEpisodes.getSlug(), partnerEpisodes.getSeriesSlug());
                  List<String> listExternalIds =
                      seriesInEpisodes.getExternalIds().stream()
                          .map(ExternalIds::getId)
                          .collect(Collectors.toList());
                  Assert.assertEquals(listExternalIds, partnerEpisodes.getExternalIdsList());
                }
              });
    }
  }

  @Test(
      expectedExceptions = RuntimeException.class,
      expectedExceptionsMessageRegExp = "INTERNAL: Partner ID does not match any existing one")
  @Description("GetItemsAll - invalid partner id")
  @TmsLink("28501171")
  public void C28501171_TEST() {
    partnersClient.getItemsAll("55fc943905dcc90000000000").getItemsList();
  }

  @Test
  @Description("GetByIds - valid partner id")
  @TmsLink("28501172")
  public void C28501172_TEST() {
    Partners partner = partnersUtils.getRandomDocument();
    List<String> listPartnerIds = List.of(partner.getId());
    List<Partner> documentsList = partnersClient.getByIds(listPartnerIds).getDocumentsList();

    Assert.assertEquals(1, documentsList.size());
    Partners grpcPartner = partnersUtils.mapGrpcObjectToPojo(documentsList.get(0), Partners.class);
    Assert.assertEquals(grpcPartner, partner);
  }

  @Test
  @Description("GetByIds - valid partner ids")
  @TmsLink("28501173")
  public void C28501173_TEST() {
    int limit = 10;
    List<Partners> listPartners = partnersUtils.getDocuments(limit);
    List<String> listPartnerIds =
        listPartners.stream().map(Partners::getId).collect(Collectors.toList());
    listPartners.sort(Comparator.comparing(Partners::getId));

    List<Partner> documentsList = partnersClient.getByIds(listPartnerIds).getDocumentsList();
    Assert.assertEquals(limit, documentsList.size());
    List<Partners> grpcPartner = partnersUtils.mapGrpcObjectsToPojo(documentsList, Partners.class);
    grpcPartner.sort(Comparator.comparing(Partners::getId));
    Assert.assertEquals(listPartners, grpcPartner);
  }

  @Test(
      expectedExceptions = RuntimeException.class,
      expectedExceptionsMessageRegExp = "NOT_FOUND: Partner documents for requested IDs not found")
  @Description("GetByIds - invalid partner id")
  @TmsLink("28501174")
  public void C28501174_TEST() {
    List<String> listPartnerIds = List.of("5796b6eb3d3a783000000000");
    partnersClient.getByIds(listPartnerIds).getDocumentsList();
  }

  @Test(
      expectedExceptions = RuntimeException.class,
      expectedExceptionsMessageRegExp = "NOT_FOUND: Partner documents for requested IDs not found")
  @Description("GetByIds - invalid partner ids")
  @TmsLink("28501175")
  public void C28501175_TEST() {
    List<String> listPartnerIds = List.of("5796b6eb3d3a783000000000", "0000b6eb3d3a783000000000");
    partnersClient.getByIds(listPartnerIds).getDocumentsList();
  }

  @Test
  @Description("GetByIds - invalid and valid partner ids")
  @TmsLink("28501176")
  public void C28501176_TEST() {
    Partners partner = partnersUtils.getRandomDocument();
    List<String> listPartnerIds = List.of(partner.getId(), "0000b6eb3d3a783000000000");
    List<Partner> documentsList = partnersClient.getByIds(listPartnerIds).getDocumentsList();
    Assert.assertEquals(1, documentsList.size());
    Partners partnerGrpc = partnersUtils.mapGrpcObjectToPojo(documentsList.get(0), Partners.class);
    Assert.assertEquals(partnerGrpc, partner);
  }

  @Test
  @Description("GetAllPaged - filter by id")
  @TmsLink("28501177")
  public void C28501177_TEST() {
    int limit = 1;
    Partners partner = partnersUtils.getRandomDocument();
    String partnerId = partner.getId();
    tv.pluto.ip.dal.partner.Partners.Filter filter =
        partnersClient.partnerFilter("_id", partnerId, "string", null);

    List<tv.pluto.ip.dal.partner.Partners.Filter> listFilters = List.of(filter);
    List<String> listProjections = List.of("advertisingId", "name");
    tv.pluto.ip.dal.partner.Partners.GetAllPagedResponse partnersAllPaged =
        partnersClient.getPartnersAllPaged(limit, 0, null, listFilters, true, listProjections);

    Assert.assertEquals(limit, partnersAllPaged.getDocumentsList().size());
    Assert.assertEquals(limit, partnersAllPaged.getDocumentsCount());
    Assert.assertEquals(limit, partnersAllPaged.getMetadata().getTotalItems());

    Partner partnerGrpc = partnersAllPaged.getDocumentsList().get(0);
    AdvertisingId AdvertisingIdGrpc =
        partnersUtils.mapGrpcObjectToPojo(partnerGrpc.getAdvertisingId(), AdvertisingId.class);

    Assert.assertEquals(partnerGrpc.getName(), partner.getName());
    Assert.assertEquals(AdvertisingIdGrpc, partner.getAdvertisingId());
    Assert.assertTrue(partnerGrpc.getCode().isEmpty());
  }

  @Test(dataProvider = "sort-values")
  @Description("GetAllPaged - sorting by name")
  @TmsLink("28501178")
  public void C28501178_TEST(int sortValue) {
    int limit = 100_000;
    String sortName = "name";
    Collation collation = Collation.builder().locale("en_US").numericOrdering(true).build();
    Bson bSort;

    if (sortValue == 1) bSort = orderBy(ascending(sortName));
    else bSort = orderBy(descending(sortName));

    DocumentQuery documentQuery =
        DocumentQuery.builder()
            .limit(MAX_LIMIT)
            .offset(0)
            .sort(bSort)
            .collation(collation)
            .projections(include("id", "name"))
            .build();

    List<Partners> mongoPartnerList = partnersUtils.getDocumentsWithCollation(documentQuery);
    List<String> listSortedOrderNames =
        mongoPartnerList.stream().map(Partners::getName).collect(Collectors.toList());

    tv.pluto.ip.dal.partner.Partners.GetAllPagedSort sort =
        tv.pluto.ip.dal.partner.Partners.GetAllPagedSort.newBuilder()
            .setField(sortName)
            .setValue(sortValue)
            .build();

    List<tv.pluto.ip.dal.partner.Partners.GetAllPagedSort> listSort = List.of(sort);
    List<String> listProjections = List.of("advertisingId", sortName);
    tv.pluto.ip.dal.partner.Partners.GetAllPagedResponse partnersAllPaged =
        partnersClient.getPartnersAllPaged(limit, 0, listSort, null, true, listProjections);

    LogUtils.logInfoMessage("Total count Partners: " + partnersAllPaged.getDocumentsCount());
    List<String> grpcPartnerList =
        partnersAllPaged.getDocumentsList().stream()
            .map(Partner::getName)
            .collect(Collectors.toList());

    Assert.assertFalse(grpcPartnerList.isEmpty());
    Assert.assertEquals(grpcPartnerList, listSortedOrderNames);
  }

  @Test
  @Description("Create - with auth token")
  @TmsLink("28501179")
  public void C28501179_TEST() {
    tv.pluto.ip.dal.partner.Partners.Partner request =
        tv.pluto.ip.dal.partner.Partners.Partner.newBuilder()
            .setName("name")
            .setCode("code")
            .setEncodingProfile("getEncodingProfile")
            .setIngestor("other")
            .build();

    Partner createResponse = partnersClientAuth.createPartner(request).getDocument();

    Assert.assertNotNull(createResponse.getId());
    Assert.assertNotNull(createResponse.getUpdatedAt());
    Assert.assertNotNull(createResponse.getCreatedAt());
    Assert.assertNotNull(createResponse.getDistributeAs());

    Assert.assertEquals(request.getName(), createResponse.getName());
    Assert.assertEquals(request.getIngestor(), createResponse.getIngestor());
    Assert.assertEquals(request.getEncodingProfile(), createResponse.getEncodingProfile());
  }

  @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "INTERNAL.*")
  @Description("Create - with auth token and empty body")
  @TmsLink("28501180")
  public void C28501180_TEST() {
    tv.pluto.ip.dal.partner.Partners.Partner request =
        tv.pluto.ip.dal.partner.Partners.Partner.newBuilder().build();
    partnersClientAuth.createPartner(request);
  }

  @Test(
      expectedExceptions = RuntimeException.class,
      expectedExceptionsMessageRegExp =
          "UNAUTHENTICATED: DB writes not allowed without authentication")
  @Description("Create - without auth token")
  @TmsLink("28501181")
  public void C28501181_TEST() {
    tv.pluto.ip.dal.partner.Partners.Partner request =
        tv.pluto.ip.dal.partner.Partners.Partner.newBuilder().setName("name").build();

    partnersClient.createPartner(request);
  }

  @Test
  @Description("UpdateFields - with auth token")
  @TmsLink("28501182")
  public void C28501182_TEST() {
    Partners partnerDb = partnersUtils.getRandomDocument();
    List<String> listFields = List.of("name", "code");

    tv.pluto.ip.dal.partner.Partners.Partner request =
        tv.pluto.ip.dal.partner.Partners.Partner.newBuilder()
            .setName(partnerDb + ".")
            .setCode("DALCode")
            .build();
    long beforeRequest = Instant.now().getEpochSecond();

    Partner updateResponse =
        partnersClientAuth.getUpdateFields(partnerDb.getId(), listFields, request).getDocument();

    Assert.assertEquals(partnerDb.getId(), updateResponse.getId());
    Assert.assertEquals(request.getName(), updateResponse.getName());
    Assert.assertEquals(request.getCode(), updateResponse.getCode());
    Assert.assertEquals(request.getParent(), updateResponse.getParent());

    long responseUpdated = Instant.parse(updateResponse.getUpdatedAt()).getEpochSecond();
    Assert.assertTrue(beforeRequest <= responseUpdated);
  }

  @Test
  @Description("UpdateFields - without token")
  @TmsLink("28501183")
  public void C28501183_TEST() {
    Partners partnerDb = partnersUtils.getRandomDocument();
    List<String> listFields = List.of("name", "code");

    tv.pluto.ip.dal.partner.Partners.Partner request =
        tv.pluto.ip.dal.partner.Partners.Partner.newBuilder()
            .setName(partnerDb + "!")
            .setCode("DALCode")
            .build();
    long beforeRequest = Instant.now().getEpochSecond();

    Partner updateResponse =
        partnersClient.getUpdateFields(partnerDb.getId(), listFields, request).getDocument();

    Assert.assertEquals(partnerDb.getId(), updateResponse.getId());
    Assert.assertEquals(request.getName(), updateResponse.getName());
    Assert.assertEquals(request.getCode(), updateResponse.getCode());
    Assert.assertEquals(request.getParent(), updateResponse.getParent());

    long responseUpdated = Instant.parse(updateResponse.getUpdatedAt()).getEpochSecond();
    Assert.assertTrue(beforeRequest <= responseUpdated);
  }

  @Test(
      expectedExceptions = RuntimeException.class,
      expectedExceptionsMessageRegExp = "INTERNAL: name is required.")
  @Description("UpdateFields - missing fields")
  @TmsLink("28501184")
  public void C28501184_TEST() {
    Partners partnerDb = partnersUtils.getRandomDocument();
    List<String> listFields = List.of("name", "code");

    tv.pluto.ip.dal.partner.Partners.Partner request =
        tv.pluto.ip.dal.partner.Partners.Partner.newBuilder().build();
    partnersClient.getUpdateFields(partnerDb.getId(), listFields, request);
  }

  @Test
  @Description("GetByApiAuthorName - valid api author name")
  @TmsLink("28501185")
  public void C28501185_TEST() {
    Partners partner = partnersUtils.getExistsAndNotEmpty("apiAuthorName");
    Assert.assertNotNull(partner);

    String apiAuthorName = partner.getApiAuthorName();
    Partner partnerClient = partnersClient.getByApiAuthorName(apiAuthorName).getDocument();
    Assert.assertEquals(apiAuthorName, partnerClient.getApiAuthorName());

    Partners mappedPartner = partnersUtils.mapGrpcObjectToPojo(partnerClient, Partners.class);
    Assert.assertEquals(partner, mappedPartner);
  }

  @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = NOT_FOUND)
  @Description("GetByApiAuthorName - invalid api author name")
  @TmsLink("28501187")
  public void C28501187_TEST() {
    String apiAuthorName = new Faker().word().toString();
    partnersClient.getByApiAuthorName(apiAuthorName);
  }

  @Test(
      expectedExceptions = RuntimeException.class,
      expectedExceptionsMessageRegExp = INTERNAL_MESSAGE)
  @Description("GetByApiAuthorName - empty api author name")
  @TmsLink("28501186")
  public void C28501186_TEST() {
    partnersClient.getByApiAuthorName("");
  }

  @Test(groups = {"ttfb"})
  @Description("Check TTFB 90 percentile for the PartnerManager GetByIds operation.")
  @TmsLink("28501188")
  public void C28501188_TEST() {

    Partners mongoPartner = partnersUtils.getRandomDocument();
    List<String> randomPartnersIds = Collections.singletonList(mongoPartner.getId());

    GrpcCall grpcCall =
        new GrpcCall(
            "PartnerManager.GetByIds", () -> partnersClient.getPartnersByIds(randomPartnersIds));

    TTFBGrpcValidator ttfbGrpcValidator = TTFBGrpcValidator.builder().build();

    ttfbGrpcValidator.validateTTFBForGrpcCalls(List.of(grpcCall));
  }

  @Test
  @Description("GetByFilter - filter by api_author_name only with is_id=false")
  @TmsLink("28501190")
  public void C28501190_TEST() {

    Partners mongoPartner = partnersUtils.getExistsAndNotEmpty("apiAuthorName");

    String apiAuthorName = mongoPartner.getApiAuthorName();

    GetByFilterResponse partnersFiltered = partnersClient.getByFilter(false, apiAuthorName, null);

    Partner partnerGrpc = partnersFiltered.getDocument();
    Partners apiPartner = partnersUtils.mapGrpcObjectToPojo(partnerGrpc, Partners.class);

    Assert.assertEquals(
        mongoPartner.getName(), apiPartner.getName(), "Partner names do not match.");
    Assert.assertEquals(
        mongoPartner.getAdvertisingId(),
        apiPartner.getAdvertisingId(),
        "Advertising IDs do not match.");
    Assert.assertEquals(
        mongoPartner.getCode(), apiPartner.getCode(), "Partner codes do not match.");
    Assert.assertEquals(
        mongoPartner.getApiAuthorName(),
        apiPartner.getApiAuthorName(),
        "Api_author_name do not match.");
  }

  @Test
  @Description("GetByFilter - filter by partner_id only with is_id=true")
  @TmsLink("28501189")
  public void C28501189_TEST() {

    Partners mongoPartner = partnersUtils.getRandomDocument();
    String partnerId = mongoPartner.getId();

    GetByFilterResponse partnersFiltered = partnersClient.getByFilter(true, null, partnerId);

    Partner partnerGrpc = partnersFiltered.getDocument();
    Partners apiPartner = partnersUtils.mapGrpcObjectToPojo(partnerGrpc, Partners.class);

    Assert.assertEquals(
        mongoPartner.getName(), apiPartner.getName(), "Partner names do not match.");
    Assert.assertEquals(
        mongoPartner.getAdvertisingId(),
        apiPartner.getAdvertisingId(),
        "Advertising IDs do not match.");
    Assert.assertEquals(
        mongoPartner.getCode(), apiPartner.getCode(), "Partner codes do not match.");
    Assert.assertEquals(mongoPartner.getId(), apiPartner.getId(), "Partner_id do not match.");
  }

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = "INVALID_ARGUMENT: PartnerId should be provided")
  @Description("GetByFilter - filter with is_id=true and missing partner_id")
  @TmsLink("28501191")
  public void C28501191_TEST() {
    boolean isId = true;
    String partnerId = null;
    String apiAuthorName = "legendfilms";

    partnersClient.getByFilter(isId, apiAuthorName, partnerId);
  }

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = "INVALID_ARGUMENT: API author name should be provided")
  @Description("GetByFilter - filter with is_id=false and missing api_author_name")
  @TmsLink("28501192")
  public void C28501192_TEST() {
    boolean isId = false;
    String apiAuthorName = null;

    Partners mongoPartner = partnersUtils.getRandomDocument();
    String partnerId = mongoPartner.getId();

    partnersClient.getByFilter(isId, apiAuthorName, partnerId);
  }

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp =
          "INTERNAL: Argument passed in must be a string of 12 bytes or a string of 24 hex characters or an integer")
  @Description("GetByFilter - filter with is_id=true and invalid partner_id format")
  @TmsLink("28501193")
  public void C28501193_TEST() {
    boolean isId = true;
    String partnerId = "invalid";
    String apiAuthorName = null;

    partnersClient.getByFilter(isId, apiAuthorName, partnerId);
  }

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp =
          "NOT_FOUND: Partner document for requested parameters not found")
  @Description("GetByFilter - filter with is_id=true and invalid apiAuthorName format")
  @TmsLink("28501194")
  public void C28501194_TEST() {
    boolean isId = false;
    String partnerId = null;
    String apiAuthorName = "invalid";

    partnersClient.getByFilter(isId, apiAuthorName, partnerId);
  }
}
