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

import io.qameta.allure.Description;
import io.qameta.allure.TmsLink;
import java.lang.reflect.InvocationTargetException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import tv.pluto.core.BaseTest;
import tv.pluto.grpc.campaign.CampaignsClient;
import tv.pluto.ip.dal.campaign.Campaigns.Filter;
import tv.pluto.ip.dal.campaign.Campaigns.GetAllPagedResponse;
import tv.pluto.ip.dal.campaign.Campaigns.GetAllPagedSort;
import tv.pluto.mongo.entities.campaigns.Campaigns;
import tv.pluto.mongo.utils.CampaignsUtils;
import tv.pluto.utils.CommonUtils;

public class CampaignsTestSuite extends BaseTest {
  private CampaignsUtils campaignsUtils;

  @BeforeClass(alwaysRun = true)
  private void setUp() {
    campaignsUtils = new CampaignsUtils();
  }

  @Test
  @Description("GetAllPaged - get all docs by active region")
  @TmsLink("28500393")
  public void C28500393_TEST() {
    String value = "US";
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    List<GetAllPagedSort> sort = campaignsClient.grpcSort("_id", 1);
    Filter filter = campaignsClient.campaignFilter("activeRegion", value, "string", null);

    List<Filter> listFilters = List.of(filter);
    List<Campaigns> listCampaigns = campaignsUtils.getDocumentsByKeyValue("activeRegion", value);
    listCampaigns.sort(Comparator.comparing(Campaigns::getId));

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, null, sort, listFilters, true);

    List<Campaigns> listGrpcCampaigns =
        campaignsUtils.mapGrpcObjectsToPojo(
            responseAllPagedGrpc.getDocumentsList(), Campaigns.class);

    Assert.assertEquals(listCampaigns.size(), responseAllPagedGrpc.getMetadata().getTotalItems());
    Assert.assertEquals(listCampaigns, listGrpcCampaigns);
  }

  @Test
  @Description("GetAllPaged - get all docs by active region with offset")
  @TmsLink("28500394")
  public void C28500394_TEST() {
    String value = "US";
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    Filter filter = campaignsClient.campaignFilter("activeRegion", value, "string", null);

    List<Filter> listFilters = List.of(filter);
    List<Campaigns> listCampaigns = campaignsUtils.getDocumentsByKeyValue("activeRegion", value);

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, 1, null, listFilters, true);
    Assert.assertEquals(
        listCampaigns.size() - 1, responseAllPagedGrpc.getMetadata().getTotalItems());
  }

  @Test
  @Description("GetAllPaged - get all docs by activeRegion with OR operator")
  @TmsLink("28500395")
  public void C28500395_TEST() {
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    List<GetAllPagedSort> sort = campaignsClient.grpcSort("_id", 1);
    Filter filterOne = campaignsClient.campaignFilter("activeRegion", "US", "string", "OR");
    Filter filterTwo = campaignsClient.campaignFilter("activeRegion", "DE", "string", "OR");
    Filter filterThree = campaignsClient.campaignFilter("activeRegion", "DAL", "string", "OR");

    List<Filter> listFilters = List.of(filterOne, filterTwo, filterThree);
    List<String> listRegions =
        listFilters.stream().map(Filter::getValue).collect(Collectors.toList());
    List<Campaigns> listCampaigns =
        campaignsUtils.getDocumentsByKeyListValues("activeRegion", listRegions);
    listCampaigns.sort(Comparator.comparing(Campaigns::getId));

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, 0, sort, listFilters, true);
    List<Campaigns> listGrpcCampaigns =
        campaignsUtils.mapGrpcObjectsToPojo(
            responseAllPagedGrpc.getDocumentsList(), Campaigns.class);

    Assert.assertEquals(listCampaigns.size(), responseAllPagedGrpc.getMetadata().getTotalItems());
    Assert.assertEquals(listCampaigns, listGrpcCampaigns);
  }

  @Test
  @Description("GetAllPaged - get all docs by name")
  @TmsLink("28500396")
  public void C28500396_TEST() {
    Campaigns campaigns = campaignsUtils.getRandomDocument();
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    List<GetAllPagedSort> sort = campaignsClient.grpcSort("_id", 1);
    Filter filter = campaignsClient.campaignFilter("name", campaigns.getName(), "string", null);

    List<Filter> listFilters = List.of(filter);
    List<Campaigns> listCampaigns =
        campaignsUtils.getDocumentsByKeyValue("name", campaigns.getName());
    listCampaigns.sort(Comparator.comparing(Campaigns::getId));

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(1, 0, sort, listFilters, true);

    List<Campaigns> listGrpcCampaigns =
        campaignsUtils.mapGrpcObjectsToPojo(
            responseAllPagedGrpc.getDocumentsList(), Campaigns.class);

    Assert.assertEquals(1, responseAllPagedGrpc.getDocumentsList().size());
    Assert.assertEquals(listCampaigns.size(), responseAllPagedGrpc.getMetadata().getTotalItems());
    Assert.assertEquals(listCampaigns.get(0), listGrpcCampaigns.get(0));
  }

  @Test
  @Description("GetAllPaged - sort by a list of distribution with OR operator")
  @TmsLink("28500397")
  public void C28500397_TEST() {
    List<Campaigns> listCampaigns = campaignsUtils.getDocuments(100);

    List<String> uniqueDistributions =
        listCampaigns.stream()
            .flatMap(
                c -> c.getDistribution() != null ? c.getDistribution().stream() : Stream.empty())
            .distinct()
            .collect(Collectors.toList());

    List<String> list = new ArrayList<>(uniqueDistributions);
    Assert.assertEquals(uniqueDistributions.size(), list.size());

    String distributionOne = uniqueDistributions.get(0);
    String distributionTwo = uniqueDistributions.get(1);

    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    List<GetAllPagedSort> sort = campaignsClient.grpcSort("_id", 1);
    Filter filterOne =
        campaignsClient.campaignFilter("distribution", distributionOne, "string", "OR");
    Filter filterTwo =
        campaignsClient.campaignFilter("distribution", distributionTwo, "string", "OR");

    List<Filter> listFilters = List.of(filterOne, filterTwo);
    List<String> listDistributions =
        listFilters.stream().map(Filter::getValue).collect(Collectors.toList());
    List<Campaigns> listCampaignsWithDistribution =
        campaignsUtils.getDocumentsByKeyListValues("distribution", listDistributions);
    listCampaignsWithDistribution.sort(Comparator.comparing(Campaigns::getId));

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, 0, sort, listFilters, true);
    List<Campaigns> listGrpcCampaigns =
        campaignsUtils.mapGrpcObjectsToPojo(
            responseAllPagedGrpc.getDocumentsList(), Campaigns.class);

    Assert.assertEquals(
        listCampaignsWithDistribution.size(), responseAllPagedGrpc.getMetadata().getTotalItems());
    Assert.assertEquals(listCampaignsWithDistribution, listGrpcCampaigns);
  }

  @Test(dataProvider = "sort-values")
  @TmsLink("28500400")
  @Description("GetAllPaged - sort by created at")
  public void C28500400_TEST(int sortValue) {
    Campaigns campaigns = campaignsUtils.getRandomDocument();
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    List<GetAllPagedSort> sort = campaignsClient.grpcSort("createdAt", sortValue);
    Filter filterOne =
        campaignsClient.campaignFilter("activeRegion", campaigns.getActiveRegion(), "string", "OR");

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, 0, sort, List.of(filterOne), true);
    List<Campaigns> listGrpcCampaigns =
        campaignsUtils.mapGrpcObjectsToPojo(
            responseAllPagedGrpc.getDocumentsList(), Campaigns.class);
    Assert.assertFalse(listGrpcCampaigns.isEmpty());

    List<LocalDateTime> listGrpc =
        listGrpcCampaigns.stream().map(Campaigns::getCreatedAt).collect(Collectors.toList());
    List<LocalDateTime> manuallySorted = new ArrayList<>(listGrpc);

    if (sortValue == 1) Collections.sort(manuallySorted);
    else Collections.sort(manuallySorted, Comparator.reverseOrder());

    Assert.assertEquals(listGrpc, manuallySorted);
  }

  @Test(dataProvider = "sort-values")
  @Description("GetAllPaged - sort by start")
  @TmsLink("28500401")
  public void C28500401_TEST(int sortValue) {
    Campaigns campaigns = campaignsUtils.getRandomDocument();
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    List<GetAllPagedSort> sort = campaignsClient.grpcSort("start", sortValue);
    Filter filterOne =
        campaignsClient.campaignFilter("activeRegion", campaigns.getActiveRegion(), "string", "OR");

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, 0, sort, List.of(filterOne), true);
    List<Campaigns> listGrpcCampaigns =
        campaignsUtils.mapGrpcObjectsToPojo(
            responseAllPagedGrpc.getDocumentsList(), Campaigns.class);
    Assert.assertFalse(listGrpcCampaigns.isEmpty());

    List<LocalDateTime> listGrpc =
        listGrpcCampaigns.stream().map(Campaigns::getStart).collect(Collectors.toList());
    List<LocalDateTime> manuallySorted = new ArrayList<>(listGrpc);

    if (sortValue == 1) Collections.sort(manuallySorted);
    else Collections.sort(manuallySorted, Comparator.reverseOrder());

    Assert.assertEquals(listGrpc, manuallySorted);
  }

  @Test(dataProvider = "sort-values")
  @Description("GetAllPaged - sort by end")
  @TmsLink("28500402")
  public void C28500402_TEST(int sortValue) {
    Campaigns campaigns = campaignsUtils.getRandomDocument();
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    List<GetAllPagedSort> sort = campaignsClient.grpcSort("end", sortValue);
    Filter filterOne =
        campaignsClient.campaignFilter("activeRegion", campaigns.getActiveRegion(), "string", "OR");

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, 0, sort, List.of(filterOne), true);
    List<Campaigns> listGrpcCampaigns =
        campaignsUtils.mapGrpcObjectsToPojo(
            responseAllPagedGrpc.getDocumentsList(), Campaigns.class);
    Assert.assertFalse(listGrpcCampaigns.isEmpty());

    List<LocalDateTime> listGrpc =
        listGrpcCampaigns.stream().map(Campaigns::getEnd).collect(Collectors.toList());
    List<LocalDateTime> manuallySorted = new ArrayList<>(listGrpc);

    if (sortValue == 1) Collections.sort(manuallySorted);
    else Collections.sort(manuallySorted, Comparator.reverseOrder());

    Assert.assertEquals(listGrpc, manuallySorted);
  }

  @Test
  @Description("GetAllPaged - filter by start")
  @TmsLink("28500403")
  public void C28500403_TEST() {
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    GetAllPagedResponse allCampaigns = campaignsClient.getAllPaged(null, 0, null, null, true);

    String startDate = allCampaigns.getDocumentsList().get(0).getStart();
    Filter filterOne = campaignsClient.campaignFilter("start", startDate, "string", "OR");
    Instant instantStartDate = Instant.parse(startDate);

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, 0, null, List.of(filterOne), true);
    Assert.assertFalse(responseAllPagedGrpc.getDocumentsList().isEmpty());

    List<Instant> listStart =
        responseAllPagedGrpc.getDocumentsList().stream()
            .map(e -> Instant.parse(e.getStart()))
            .collect(Collectors.toList());

    listStart.forEach(
        c -> {
          boolean isStart = CommonUtils.dateGteOrEqual(c, instantStartDate);
          Assert.assertTrue(isStart);
        });
  }

  @Test
  @Description("GetAllPaged - filter by start and end")
  @TmsLink("28500406")
  public void C28500406_TEST() {
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    GetAllPagedResponse allCampaigns = campaignsClient.getAllPaged(null, 0, null, null, true);

    tv.pluto.ip.dal.campaign.Campaigns.Campaign campaign = allCampaigns.getDocumentsList().get(0);
    String startCreated = campaign.getCreatedAt();
    Instant instantStart = Instant.parse(startCreated);
    Instant instantEnd = instantStart.plusSeconds(999);

    Filter filterOne =
        campaignsClient.campaignFilter("createdAtStart", startCreated, "string", null);
    Filter filterTwo =
        campaignsClient.campaignFilter("createdAtEnd", instantEnd.toString(), "string", null);

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, 0, null, List.of(filterOne, filterTwo), true);
    Assert.assertFalse(responseAllPagedGrpc.getDocumentsList().isEmpty());
    responseAllPagedGrpc
        .getDocumentsList()
        .forEach(
            c -> {
              Instant createdAt = Instant.parse(c.getCreatedAt());
              boolean isDateBetween =
                  CommonUtils.isDateBetweenDatesOrEqual(instantStart, instantEnd, createdAt);
              Assert.assertTrue(isDateBetween);
            });
  }

  @Test
  @Description("GetAllPaged - filter by end")
  @TmsLink("28500404")
  public void C28500404_TEST() {
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    GetAllPagedResponse allCampaigns = campaignsClient.getAllPaged(null, 0, null, null, true);

    String endDate = allCampaigns.getDocumentsList().get(0).getEnd();
    Filter filterOne = campaignsClient.campaignFilter("end", endDate, "string", "OR");
    Instant instantEnd = Instant.parse(endDate);

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, 0, null, List.of(filterOne), true);
    Assert.assertFalse(responseAllPagedGrpc.getDocumentsList().isEmpty());

    List<Instant> listStart =
        responseAllPagedGrpc.getDocumentsList().stream()
            .map(e -> Instant.parse(e.getStart()))
            .collect(Collectors.toList());

    listStart.forEach(
        c -> {
          boolean isEnd = CommonUtils.dateEqualOrLess(c, instantEnd);
          Assert.assertTrue(isEnd);
        });
  }

  @Test
  @Description("GetAllPaged - filter by createdAt")
  @TmsLink("28500405")
  public void C28500405_TEST() {
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    GetAllPagedResponse allCampaigns = campaignsClient.getAllPaged(null, 0, null, null, true);

    String createdDate = allCampaigns.getDocumentsList().get(0).getCreatedAt();
    Filter filterOne = campaignsClient.campaignFilter("createdAt", createdDate, "string", "OR");

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, 0, null, List.of(filterOne), true);
    Assert.assertFalse(responseAllPagedGrpc.getDocumentsList().isEmpty());
    responseAllPagedGrpc
        .getDocumentsList()
        .forEach(c -> Assert.assertEquals(createdDate, c.getCreatedAt()));
  }

  @Test
  @Description("GetAllPaged - filter by published")
  @TmsLink("28500407")
  public void C28500407_TEST() {
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    GetAllPagedResponse allCampaigns = campaignsClient.getAllPaged(null, 0, null, null, true);

    Boolean isPublished = allCampaigns.getDocumentsList().get(0).getPublished();
    Filter filter =
        campaignsClient.campaignFilter("published", isPublished.toString(), "string", null);

    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPaged(null, 0, null, List.of(filter), true);
    Assert.assertFalse(responseAllPagedGrpc.getDocumentsList().isEmpty());
    responseAllPagedGrpc
        .getDocumentsList()
        .forEach(c -> Assert.assertEquals(isPublished, c.getPublished()));
  }

  @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = NOT_FOUND)
  @TmsLink("28500398")
  @Description("GetAllPaged - invalid distribution field")
  public void C28500398_TEST()
      throws NoSuchFieldException, InvocationTargetException, IllegalAccessException {
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    Filter filterOne = campaignsClient.campaignFilter("distribution", "testDal135", "string", "OR");
    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPagedException(null, 0, null, List.of(filterOne), true);
    Assert.assertNull(responseAllPagedGrpc);
  }

  @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = NOT_FOUND)
  @TmsLink("28500399")
  @Description("GetAllPaged - invalid activeRegion field")
  public void C28500399_TEST()
      throws NoSuchFieldException, InvocationTargetException, IllegalAccessException {
    CampaignsClient campaignsClient = new CampaignsClient(grpcChannelManager.getChannel());
    Filter filterOne = campaignsClient.campaignFilter("activeRegion", "testDal135", "string", "OR");
    GetAllPagedResponse responseAllPagedGrpc =
        campaignsClient.getAllPagedException(null, 0, null, List.of(filterOne), true);
    Assert.assertNull(responseAllPagedGrpc);
  }
}
