package io.delphi.qa.auto.awsmfrw.managers.helpers.common;

import com.google.common.base.Charsets;
import com.google.common.base.Functions;
import com.google.common.collect.Ordering;
import io.delphi.qa.auto.awsmfrw.common_baby.IsaCommonMethods;
import io.delphi.qa.auto.awsmfrw.common_baby.IsaConstants;
import io.qameta.allure.Allure;
import io.qameta.allure.Step;
import io.restassured.response.ValidatableResponse;
import org.testng.Assert;

public interface IsaAssertions extends IsaConstants, IsaCommonMethods {

  Ordering<Object> OBJ_ORDERING =
      Ordering.from(String.CASE_INSENSITIVE_ORDER)
          .onResultOf(Functions.toStringFunction())
          .onResultOf(Functions.constant(Charsets.UTF_8));

  static String indentMessage(String msg) {
    return "\n\t" + msg + "\n\t\t";
  }

  default void isaAssert(String message, Allure.ThrowableRunnableVoid runnable) {
    Allure.step(message, runnable);
  }

  @Deprecated
  default <T> void isaAssertEquals(T actual, T expected) {
    Allure.step(
        String.format(
            "[%s]assert: actual=%s should be equal to expected=%s",
            envConfig.env(), actual, expected),
        () -> Assert.assertEquals(actual, expected));
  }

  @Deprecated
  static <T> void isaStaticAssertEquals(T actual, T expected) {
    Allure.step(
        String.format(
            "[%s]assert: actual=%s should be equal to expected=%s",
            envConfig.env(), actual, expected),
        () -> Assert.assertEquals(actual, expected));
  }

  default <T> void isaAssertEquals(T actual, T expected, String msg) {
    String indentMessage = indentMessage(msg);
    Allure.step(
        String.format(
            "[%s]assert: actual=%s should be equal to expected=%s",
            envConfig.env(), actual, expected),
        () -> Assert.assertEquals(actual, expected, indentMessage));
  }

  default <T> void isaAssertEqualsFloat(float actual, float expected, float delta, String msg) {
    String indentMessage = indentMessage(msg);
    Allure.step(
            String.format(
                    "[%s]assert: actual=%s should be equal to expected=%s",
                    envConfig.env(), actual, expected),
            () -> Assert.assertEquals(actual, expected, delta, indentMessage));
  }

  default <T> void isaAssertSame(T actual, T expected, String msg) {
    String indentMessage = indentMessage(msg);
    Allure.step(
            String.format(
                    "[%s]assert: actual=%s should be equal to expected=%s",
                    envConfig.env(), actual, expected),
            () -> Assert.assertSame(actual, expected, indentMessage));
  }

  default <T> void isaAssertEqualsNew(T actual, T expected, String msg) {
    Assert.assertEquals(
        actual,
        expected,
        String.format(
            "[%s]assert: %s: actual=%s should be equal to expected=%s",
            envConfig.env(), indentMessage(msg), actual, expected));
  }

  static <T> void isaStaticAssertEquals(T actual, T expected, String msg) {
    String indentMessage = indentMessage(msg);
    Allure.step(
        String.format(
            "[%s]assert: actual=%s should be equal to expected=%s",
            envConfig.env(), actual, expected),
        () -> Assert.assertEquals(actual, expected, indentMessage));
  }

  @Deprecated
  default void isaAssertFalse(boolean condition) {
    Allure.step(
        String.format("[%s]assert: condition is %s", envConfig.env(), condition),
        () -> Assert.assertFalse(condition));
  }

  default void isaAssertFalse(boolean condition, String msg) {
    String indentMessage = indentMessage(msg);
    Allure.step(
        String.format("[%s]assert: condition is %s", envConfig.env(), indentMessage),
        () -> Assert.assertFalse(condition, indentMessage));
  }

  default <T> void isaAssertNotEquals(T actual, T expected, String msg) {
    String indentMessage = indentMessage(msg);
    Allure.step(
        String.format(
            "[%s]assert: actual=%s should not be equal to expected=%s",
            envConfig.env(), actual, expected),
        () -> Assert.assertNotEquals(actual, expected, indentMessage));
  }

  default <T> void isaAssertNotEqualsNew(T actual, T expected, String msg) {
    Assert.assertNotEquals(actual, expected, indentMessage(msg));
  }

  default <T> void isaAssertNotNull(T valueUnderTest, String msg) {
    String indentMessage = indentMessage(msg);
    Allure.step(
        String.format("[%s]assert: %s is not null", envConfig.env(), indentMessage),
        () -> Assert.assertNotNull(valueUnderTest, indentMessage));
  }

  @Step
  default <T> void isaAssertNotNullNew(T valueUnderTest, String msg) {
    Assert.assertNotNull(
        valueUnderTest,
        String.format("[%s]assert: %s is not null", envConfig.env(), indentMessage(msg)));
  }

  default <T> void isaAssertNull(T valueUnderTest, String msg) {
    String indentMessage = indentMessage(msg);
    Allure.step(
        String.format("[%s]assert: %s is not null", envConfig.env(), indentMessage),
        () -> Assert.assertNull(valueUnderTest, indentMessage));
  }

  default <T> void isaAssertNullNew(T valueUnderTest, String msg) {
    Assert.assertNull(valueUnderTest,  indentMessage(msg));
  }

  default void isaAssertOrderingAsc(Iterable<? extends Comparable> collect, String value) {
    String msg = "expected ordering asc";
    String indentMessage = indentMessage(msg);
    String format = String.format("[%s]assert: condition is %s", envConfig.env(), indentMessage);
    Allure.step(
        format,
        () -> {
          Assert.assertEquals(
              collect,
              OBJ_ORDERING.nullsLast().sortedCopy(collect),
              String.format(
                  "\n\tvalue=%s\n\tcollection=%s\n\treason=%s\n\t", value, collect, indentMessage));
        });
  }

  default void isaAssertOrderingDesc(Iterable<? extends Comparable> collect, String value) {
    String msg = "expected ordering desc";
    String indentMessage = indentMessage(msg);
    Allure.step(
        String.format("[%s]assert: condition is %s", envConfig.env(), indentMessage),
        () -> {
          Assert.assertEquals(
              collect,
              OBJ_ORDERING.reverse().nullsFirst().sortedCopy(collect),
              String.format(
                  "\n\tvalue=%s\n\tcollection=%s\n\treason=%s\n\t", value, collect, indentMessage));
        });
  }

  @Deprecated
  default void isaAssertTrue(boolean condition) {
    Allure.step(
        String.format("[%s]assert: condition is %s", envConfig.env(), condition),
        () -> Assert.assertTrue(condition));
  }

  default void isaAssertTrue(boolean condition, String msg) {
    String indentMessage = indentMessage(msg);
    Allure.step(
        String.format("[%s]assert: condition is %s", envConfig.env(), condition, indentMessage),
        () -> {
          Assert.assertTrue(condition, indentMessage);
        });
  }

  @Step
  default void isaAssertTrueNew(boolean condition, String msg) {
    Assert.assertTrue(
        condition,
        String.format(
            "[%s]assert: condition is %s:\n%s", envConfig.env(), condition, indentMessage(msg)));
  }

  default <T> T isaStep(Class<T> plStreamsClass, T as) {
    return Allure.step(
        String.format(
            "[%s]step: rest call with payload=%s", envConfig.env(), plStreamsClass.getSimpleName()),
        () -> as);
  }

  default <T> void isaStep(Class<T> clazz, ValidatableResponse validatableResponse) {
    Allure.step(
        String.format(
            "[%s]step: rest call with payload=%s", envConfig.env(), clazz.getSimpleName()),
        () -> validatableResponse);
  }
}
