package tv.pluto;

import com.mongodb.client.result.InsertOneResult;
import com.plutotv.test.lib.LogUtils;
import io.qameta.allure.Description;
import io.qameta.allure.TmsLink;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import tv.pluto.core.BaseTest;
import tv.pluto.grpc.cron_history.CronHistoryClient;
import tv.pluto.ip.dal.cronhistory.CronHistoryOuterClass;
import tv.pluto.mongo.entities.cronhistories.CronHistory;
import tv.pluto.mongo.utils.CronHistoriesUtils;

public class CronHistoryTestSuite extends BaseTest {
  private CronHistoriesUtils cronHistoriesUtils;
  private CronHistoryClient cronHistoryClient;
  private String newId;

  @BeforeClass(alwaysRun = true)
  private void setUp() {
    cronHistoriesUtils = new CronHistoriesUtils();
    cronHistoryClient = new CronHistoryClient(grpcChannelManager.getChannel());
    InsertOneResult cronHiStoryDoc = cronHistoriesUtils.createCronHiStoryDoc();
    newId =
        Objects.requireNonNull(cronHiStoryDoc.getInsertedId()).asObjectId().getValue().toString();
    Assert.assertNotNull(newId);
  }

  @Test(dataProvider = "gte-lte")
  @TmsLink("28500732")
  @Description("GetCronHistory - verify all values")
  public void C28500732_TEST(String operator) {
    CronHistory cronHistory = cronHistoriesUtils.getDocumentById(newId);

    Instant time = cronHistory.getRanAt().toInstant(ZoneOffset.UTC);
    LogUtils.logInfoMessage("C33796606. Time: " + time);

    List<CronHistoryOuterClass.CronHistory> historyList =
        cronHistoryClient.getCronHistory(time.toString(), operator);

    List<CronHistory> grpcListHistories =
        cronHistoriesUtils.mapGrpcObjectsToPojo(historyList, CronHistory.class);

    List<CronHistory> getListHistoryDb =
        cronHistoriesUtils.getDocumentsFieldTimeOperator("ranAt", time, "$" + operator);
    grpcListHistories.sort(Comparator.comparing(CronHistory::getId));
    getListHistoryDb.sort(Comparator.comparing(CronHistory::getId));

    Assert.assertFalse(getListHistoryDb.isEmpty());
    Assert.assertEquals(getListHistoryDb, grpcListHistories);
  }

  @Test
  @Description("GetCronHistory - verify all values when operator is null")
  @TmsLink("28500733")
  public void C28500733_TEST() {
    CronHistory cronHistory = cronHistoriesUtils.getDocumentById(Objects.requireNonNull(newId));

    Instant time = cronHistory.getRanAt().toInstant(ZoneOffset.UTC);
    LogUtils.logInfoMessage("C33796607. Time: " + time);

    List<CronHistoryOuterClass.CronHistory> historyList =
        cronHistoryClient.getCronHistory(time.toString(), null);

    List<CronHistory> grpcListHistories =
        cronHistoriesUtils.mapGrpcObjectsToPojo(historyList, CronHistory.class);

    List<CronHistory> getListHistoryDb = cronHistoriesUtils.getAllDocuments();

    grpcListHistories.sort(Comparator.comparing(CronHistory::getId));
    getListHistoryDb.sort(Comparator.comparing(CronHistory::getId));

    Assert.assertFalse(getListHistoryDb.isEmpty());
    Assert.assertEquals(getListHistoryDb, grpcListHistories);
  }

  @Test(
      expectedExceptions = RuntimeException.class,
      expectedExceptionsMessageRegExp = "FAILED_PRECONDITION.*")
  @Description("GetCronHistory - invalid operator")
  @TmsLink("28500734")
  public void C28500734_TEST() {
    cronHistoryClient.getCronHistory(null, "test");
  }

  @Test
  @Description("UpdateFields - update ranAt")
  @TmsLink("28500735")
  public void C28500735_TEST() {
    // the test is not stable because of the test data. Test data should be unique and from enum
    // schema.
    CronHistory cronHistory =
        cronHistoriesUtils.getDocumentByKeyValue("jobType", "elastic-index-clips");
    //        CronHistory cronHistory = cronHistoriesUtils.getRandomCronNotTestData();
    Assert.assertNotNull(
        cronHistory, "CronHistory not found. Need real data for testing with job_type enum");

    String newId = cronHistory.getId();
    Instant timeNow = Instant.now().truncatedTo(ChronoUnit.SECONDS);
    String timeNowString = timeNow.toString();

    CronHistoryOuterClass.CronHistory cronHistoryDoc =
        CronHistoryOuterClass.CronHistory.newBuilder().setRanAt(timeNowString).build();

    // enum job type. Won't be able to update any test-data other than from schema
    CronHistoryOuterClass.CronHistory updatedGrpc =
        cronHistoryClient.updateFields(newId, cronHistoryDoc, List.of("ranAt"));
    Assert.assertEquals(cronHistory.getId(), updatedGrpc.getId());
    Assert.assertEquals(
        cronHistory.getCreatedAt().toInstant(ZoneOffset.UTC),
        Instant.parse(updatedGrpc.getCreatedAt()));

    CronHistory updatedDb = cronHistoriesUtils.getDocumentById(newId);
    Instant updatedRanAtGrpc =
        Instant.parse(updatedGrpc.getRanAt()).truncatedTo(ChronoUnit.SECONDS);
    Instant updatedRanAtDb =
        updatedDb.getRanAt().toInstant(ZoneOffset.UTC).truncatedTo(ChronoUnit.SECONDS);

    Assert.assertEquals(updatedRanAtGrpc, timeNow.truncatedTo(ChronoUnit.SECONDS));
    Assert.assertEquals(updatedRanAtDb, timeNow);

    Assert.assertEquals(cronHistory.getId(), updatedDb.getId());
    Assert.assertEquals(cronHistory.getJobType(), updatedDb.getJobType());
    Assert.assertEquals(cronHistory.getCreatedAt(), updatedDb.getCreatedAt());
  }

  @Test(
      expectedExceptions = RuntimeException.class,
      expectedExceptionsMessageRegExp = INTERNAL_MESSAGE)
  @Description("UpdateFields - invalid job type")
  @TmsLink("28500736")
  public void C28500736_TEST() {
    CronHistory cronHistory = cronHistoriesUtils.getRandomDocument();

    CronHistoryOuterClass.CronHistory cronHistoryDoc =
        CronHistoryOuterClass.CronHistory.newBuilder().setJobType("test").build();

    cronHistoryClient.updateFields(cronHistory.getId(), cronHistoryDoc, List.of("jobType"));
  }

  @AfterTest
  public void afterTest() {
    cronHistoriesUtils.deleteDocumentById(newId);
  }
}
