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

import com.plutotv.test.lib.LogUtils;
import com.plutotv.test.listeners.TestRailReporterListener;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Parameters;
import tv.pluto.mongo.utils.MongoUtils;
import tv.pluto.utils.Route;
import tv.pluto.utils.YamlUtils;

@ContextConfiguration(classes = GrpcConfig.class)
public abstract class BaseTest extends AbstractTestNGSpringContextTests {
  protected static final String SERVICE_NAME = "service-dal";
  protected static final String SERVICE_VERSION = YamlUtils.getAppVersion();
  @Autowired public GrpcChannelManager grpcChannelManager; // NOSONAR
  protected static final int DEFAULT_LIMIT = 1000;
  protected static final int MAX_LIMIT = 999_999_999;
  protected static final String INVALID_ID = "000ee000ee0a000000000000";
  protected LocalDate startDateTime = LocalDate.of(1990, 1, 1);
  protected LocalDate endDateTime = LocalDate.now(ZoneOffset.UTC);
  protected final String failStartDateTime = LocalDate.of(1980, 6, 23).toString();
  protected final String failEndDateTime = LocalDate.of(1981, 6, 23).toString();

  protected static final String NOT_FOUND_MESSAGE = "NOT_FOUND: Not found";
  protected static final String NOT_FOUND = "NOT_FOUND.*";
  protected static final String INTERNAL_MESSAGE = "INTERNAL.*";
  protected static final String INVALID_ARGUMENT_MESSAGE =
      "INVALID_ARGUMENT: "
          + "Invalid parameters provided. 'updatedAtMin' and 'updatedAtMax' must be valid Date objects.";
  protected static final String INTERNAL_ERROR_MESSAGE =
      "INVALID_ARGUMENT: " + "Error: updatedAtMax value is less than or equal to updatedAtMin.";

  private static final Object[][] FAIL_CONTENT_FEEDS =
      new Object[][] {
        {"2021-40-40T00:00:00.000Z", "2020-06-23T00:00:00.000Z"},
        {"2020-06-23T00:00:00.000Z", "2021-40-40T00:00:00.000Z"},
        {"test", "2020-06-23T00:00:00.000Z"},
        {"2020-06-23T00:00:00.000Z", "test"},
        {"", "2020-06-23T00:00:00.000Z"},
        {"2020-06-23T00:00:00.000Z", ""},
      };

  private static final Object[][] FAIL_CONTENT_FEEDS_LESS_OR_EQUAL =
      new Object[][] {
        {"2020-06-23", "2020-05-23"},
        {"2020-06-23", "2020-06-23"}
      };

  @DataProvider(name = "name-and-displayName")
  public Object[][] nameAndDisplay() {
    return new Object[][] {{"name"}, {"displayName"}};
  }

  @DataProvider(name = "gte-lte")
  public Object[][] gteLte() {
    return new Object[][] {{"gte"}, {"lte"}};
  }

  @DataProvider(name = "is-true-false")
  public static Object[][] isTrueFalse() {
    return new Object[][] {{true}, {false}};
  }

  @DataProvider(name = "fail-content-feeds-test-data")
  public Object[][] dataProvider() {
    return FAIL_CONTENT_FEEDS;
  }

  @DataProvider(name = "natural-custom")
  public Object[][] naturalCustom() {
    return new Object[][] {{"natural"}, {"custom"}};
  }

  @DataProvider(name = "start-stop-channel")
  public Object[][] startStopChannel() {
    return new Object[][] {{"start"}, {"stop"}, {"channel"}};
  }

  @DataProvider(name = "custom-null")
  public Object[][] customAndNull() {
    return new Object[][] {{"custom"}, {null}};
  }

  @DataProvider(name = "less-or-equal-fail-content-feeds-test-data")
  public Object[][] dataProviderLessOrEqual() {
    return FAIL_CONTENT_FEEDS_LESS_OR_EQUAL;
  }

  @DataProvider(name = "enabled-plutoOfficeOnly-kidsMode-enabled")
  public Object[][] enabledPlutoOfficeOnlyKidsModeEnabled() {
    return new Object[][] {{"enabled"}, {"plutoOfficeOnly"}, {"kidsMode"}, {"enabled"}};
  }

  @DataProvider(name = "regionFilter-distribution")
  public Object[][] filterFields() {
    return new Object[][] {
      {"regionFilter.include"},
      {"regionFilter.exclude"},
      {"distribution.include"},
      {"distribution.exclude"}
    };
  }

  @DataProvider(name = "sort-values")
  public Object[][] sortValues() {
    return new Object[][] {{1}, {-1}};
  }

  public Object[][] fileDataProvider(String fileName) {
    List<String> lines;
    try {
      lines = Files.readAllLines(Paths.get("src/test/resources/data-provider-data/" + fileName));
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    Object[][] data = new Object[lines.size()][2];
    for (int i = 0; i < lines.size(); i++) {
      String[] parts = lines.get(i).split(",");
      data[i][0] = parts[0].trim();
      data[i][1] = Integer.parseInt(parts[1].trim());
    }
    return data;
  }

  @DataProvider(name = "get-vod-category-list-sort")
  public Object[][] getVodCategoryListSort() {
    return fileDataProvider("get-vod-category-list-sort.txt");
  }

  @DataProvider(name = "published-plutoOffice-kidsMode-name-sort")
  public Object[][] publishedPlutoOfficeKidsModeNameSort() {
    return fileDataProvider("published-plutoOffice-kidsMode-name-sort.txt");
  }

  @DataProvider(name = "sort-values-true-false")
  public Object[][] sortValuesTrueFalse() {
    return new Object[][] {
      {1, true},
      {1, false},
      {-1, true},
      {-1, false}
    };
  }

  @Parameters("app.url")
  @BeforeSuite(alwaysRun = true)
  public void beforeSuite(String appUrl) {
    LogUtils.logInfoMessage("Start of Test Suite");

    Route.setEnvironment(appUrl);

    TestRailReporterListener.setAppVersion(SERVICE_VERSION);

    if (Route.isNonProdEnvironment()) MongoUtils.cleanUpDbTestData();

    // DAL Server Address
    LogUtils.logInfoMessage("DAL Server Address: " + Route.getGrpcServer());
  }

  @AfterSuite
  public void afterSuite() {
    MongoDbManager.cleanupConnection();
    LogUtils.logInfoMessage("End of Test Suite");
  }
}
