package io.delphi.qa.auto.awsmfrw.tests;

import io.delphi.qa.auto.awsmfrw.common_baby.IsaCommonMethods;
import io.delphi.qa.auto.awsmfrw.common_baby.IsaLogging;
import io.delphi.qa.auto.awsmfrw.common_baby.delphi.aws.EnAwsSecrets;
import io.delphi.qa.auto.awsmfrw.common_baby.delphi.aws.ICanFetchAwsSecret;
import io.delphi.qa.auto.awsmfrw.managers.helpers.api.rest.AbstractRestAssuredClient;
import io.delphi.qa.auto.awsmfrw.models.api.ICanBuildApiTestByString;
import io.delphi.qa.auto.awsmfrw.models.api.rest.v3.health.PlHealth;
import io.delphi.qa.auto.awsmfrw.suppliers.DelphiDataContainer;
import io.qameta.allure.Allure;
import io.qameta.allure.Step;
import org.apache.commons.io.FileUtils;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;

import java.io.*;
import java.net.URL;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

public abstract class BsTestCase extends BsTest
    implements ICanFetchAwsSecret, ICanBuildApiTestByString {

  protected final ThreadLocal<Integer> counters = new ThreadLocal<>();

  private final String STR_TEST_RUN_ID;
  // TODO by alexeyisakov @4/22/21#12:22 PM: Rm unless statement
  protected final Map<String, String> delphiApiDbSecret;
  private final ThreadLocal<String> TL_TEST_CASE_NAME = new ThreadLocal<>();
  protected ThreadLocal<AbstractRestAssuredClient> bsRestAssured = new ThreadLocal<>();

  {
    delphiApiDbSecret = fetchAwsSecret(EnAwsSecrets.DELPHI_API_DB_PARAMS);
  }

  protected String getTestRunId() {
    return STR_TEST_RUN_ID;
  }

  public BsTestCase() {
    this.STR_TEST_RUN_ID = genTestRunId();
  }

  protected abstract String genTestRunId();

  @BeforeSuite(alwaysRun = true)
  protected void defineDefaultEnvironment() {
    PlHealth health = new PlHealth().withEnvironment(envConfig.env()).withStatus("DEFAULT");
    DelphiDataContainer.MAP_ENV_HEALTH.put("health", health);
  }

  @AfterSuite(alwaysRun = true)
  protected void reportEnvironment() {
    Properties gradleProps = IsaCommonMethods.loadProperties("allure.properties");
    final String environmentFileName = "environment.properties";
    try (FileOutputStream fos =
        new FileOutputStream(
            String.format(
                "%s/%s",
                gradleProps.getProperty("allure.results.directory"), environmentFileName))) {
      Properties properties = IsaCommonMethods.loadProperties("props/local.properties");
      Properties output = new Properties();
      properties.entrySet().stream()
          .filter(f -> f.getKey().toString().startsWith("delphi.env.run"))
          .forEach(
              o ->
                  output.setProperty((String) o.getKey(), System.getProperty((String) o.getKey())));
      output.store(fos, "See https://docs.qameta.io/allure/#_environment");
    } catch (Throwable e) {
      IsaLogging.log(
          this.getClass(),
          String.format("IO problem when writing allure %s file", environmentFileName));
    }
    final String categoriesFileName = "categories.json";
    String outputCategoriesLocator =
        String.format(
            "%s/%s", gradleProps.getProperty("allure.results.directory"), categoriesFileName);
    try (FileWriter fw = new FileWriter(outputCategoriesLocator)) {
      URL resource = Thread.currentThread().getContextClassLoader().getResource(categoriesFileName);
      String fileLocator = Objects.requireNonNull(resource).getPath();
      String str = IsaCommonMethods.loadFile2String(new File(fileLocator));
      fw.append(str);
      fw.flush();
    } catch (IOException e) {
      IsaLogging.log(
          this.getClass(),
          String.format("IO problem when writing allure %s file", categoriesFileName));
    }
  }

  @AfterMethod(alwaysRun = true, description = "Attach test case log")
  public void postM_processLogFile(ITestResult iTestResult) {
    if (envConfig.isLogAll()) {
      if (iTestResult.getStatus() != ITestResult.SUCCESS) {
        final File finalFile =
            new File(String.format("./build/logs/%s.log", TL_TEST_CASE_NAME.get()));
        IsaCommonMethods.doWithWait(
            () -> {
              try (FileInputStream fis =
                  FileUtils.openInputStream(Objects.requireNonNull(finalFile))) {
                Allure.addAttachment(finalFile.getName(), fis);
                fis.close();
                return true;
              } catch (IOException e) {
                return false;
              }
            },
            10,
            123,
            TimeUnit.MILLISECONDS);
      }
    }
    stopTestCaseLog();
  }

  @BeforeMethod(alwaysRun = true, description = "Test case logger")
  public void preM_startTestCaseLog(ITestResult iTestResult) {
    TL_TEST_CASE_NAME.set(
        String.format(
            "%s-%s", STR_TEST_RUN_ID, iTestResult.getMethod().toString().split("\\(")[0]));
    log().info("\n- test case name:" + TL_TEST_CASE_NAME.get());
    if (envConfig.isLogAll()) {
      // start logging to test specific log file
      startTestCaseLog(TL_TEST_CASE_NAME.get());
      log().info("\n\t- logger started");
    } else
      log()
          .info(
              "\n\t- status: logger terned off (add system property 'delphi.env.run.type.isLogAll=true')");
  }

  @Step("Build API Rest request by method name")
  protected void buildApiRestRequest(Object[] dataProviderParams, String name) {
    AbstractRestAssuredClient.CustomRequestSpec rqSpec =
        buildRestRequestSpec(name, dataProviderParams);
    bsRestAssured.set(delphi.api().rest().javaApi().withRequestSpecification(rqSpec));
  }
}
