package tv.pluto.utils;

import com.plutotv.test.lib.LogUtils;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import lombok.experimental.UtilityClass;
import org.assertj.core.util.Streams;
import org.testng.Assert;

@UtilityClass
public class CommonUtils {

  public static void printTestCaseNameWithField(String method, String field) {
    LogUtils.logInfoMessage(method + ", testField: " + field);
  }

  public static void printTestCaseNameWithId(String method, String id) {
    LogUtils.logInfoMessage(method + ", id: " + id);
  }

  public static String generateRandomObjectId() {
    return UUID.randomUUID().toString().replace("-", "").substring(0, 24);
  }

  public static String localDateTimeStringPlusDays(int days) {
    return LocalDateTime.now(ZoneOffset.UTC).plusDays(days) + "Z";
  }

  public static LocalDateTime getNowDateTimeUtc() {
    return LocalDateTime.now(ZoneOffset.UTC);
  }

  public static List<?> iteratorToList(Iterator<?> iterator) {
    return Streams.stream(iterator).collect(Collectors.toList());
  }

  @SuppressWarnings("unchecked")
  public static <T> T iteratorToListOf(Iterator<?> iterator) {
    return (T) Streams.stream(iterator).collect(Collectors.toList());
  }

  public static boolean isTwoListsEqual(List<String> listOne, List<String> listTwo) {
    if (listOne.isEmpty()) {
      LogUtils.logSevereMessageThenFail("The first list has 0 elements");
      return false;
    }

    Collections.sort(listOne);
    Collections.sort(listTwo);
    return listOne.equals(listTwo);
  }

  public static String slightlyModifyString(String text) {
    if (text == null) return "Test";
    else if (text.endsWith(".")) return text.substring(0, text.length() - 1).trim();

    return text + ".";
  }

  public static boolean isDateBetweenDates(LocalDateTime dateBefore, LocalDateTime updateTime) {
    return updateTime.isAfter(dateBefore) && updateTime.isBefore(LocalDateTime.now(ZoneOffset.UTC));
  }

  public static String getRandomValueFromList(List<String> list) {
    int index = 0;

    if (list.isEmpty()) Assert.fail("The list with values is empty");
    else if (list.size() > 1) index = new Random().nextInt(list.size() - 1);

    LogUtils.logInfoMessage("Selected value from list: " + list.get(index));
    return list.get(index);
  }

  public static boolean isDateBetweenDates(
      Instant dateBefore, Instant dateAfter, Instant testTime) {
    return dateAfter.isAfter(dateBefore) && testTime.isBefore(dateAfter);
  }

  public static boolean isDateBetweenDatesOrEqual(
      Instant dateBefore, Instant dateAfter, Instant testTime) {
    return !testTime.isBefore(dateBefore) && !testTime.isAfter(dateAfter);
  }

  public static int getRandomNumber(int number) {
    return new Random().nextInt(number) + 1;
  }

  public static String getRandomString() {
    String newField = "testDal" + UUID.randomUUID();
    LogUtils.logInfoMessage("New field: " + newField);
    return newField;
  }

  public static Object[][] getRegexpSearch(String searchStr) {
    String exactMatch = "^" + searchStr + "$";
    String startWith = "^" + searchStr.substring(0, searchStr.length() / 2) + ".*";
    String endWith = ".*" + searchStr.substring(searchStr.length() / 2) + "$";
    // Match unknown single character
    String unkSingleChr = replaceChar(searchStr, '.', searchStr.length() / 2);

    return new Object[][] {{exactMatch}, {startWith}, {endWith}, {unkSingleChr}};
  }

  public static String replaceChar(String str, char ch, int index) {
    return str.substring(0, index) + ch + str.substring(index + 1);
  }

  public static boolean containsSpecialCharacters(String input) {
    // Define a regular expression pattern to match special characters
    String pattern = "[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>/?]";

    // Use the matches() method to check if the input string contains any special characters
    return input.matches(".*" + pattern + ".*");
  }

  public static List<Integer> listMissingIntegerFromListInOrder(List<Integer> list, int limit) {
    return IntStream.rangeClosed(1, 9000)
        .filter(i -> !list.contains(i))
        .limit(limit)
        .boxed()
        .collect(Collectors.toList());
  }

  public static boolean dateGteOrEqual(Instant testTime, Instant dateAfter) {
    return testTime.isAfter(dateAfter) || testTime.equals(dateAfter);
  }

  public static boolean dateEqualOrLess(Instant testTime, Instant dateBefore) {
    return dateBefore.isAfter(testTime) || dateBefore.equals(testTime);
  }

  public static class NumericStringComparator implements Comparator<String> {
    @Override
    public int compare(String s1, String s2) {
      s1 = s1.trim().toLowerCase();
      s2 = s2.trim().toLowerCase();

      boolean isS1Numeric = s1.matches("\\d+");
      boolean isS2Numeric = s2.matches("\\d+");

      if (isS1Numeric && isS2Numeric) {
        return Integer.compare(Integer.parseInt(s1), Integer.parseInt(s2));
      }

      if (isS1Numeric) {
        return 1;
      }
      if (isS2Numeric) {
        return -1;
      }

      return s1.compareTo(s2);
    }
  }
}
