package com.bonus.canteen.core.common.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

public class JacksonUtil {
   private static final ObjectMapper objectMapper;
   private static final ObjectMapper objectMapperIgnoreNull;
   private static final Logger logger;

   public static ObjectMapper getInstance() {
      return objectMapper;
   }

   public static ObjectMapper getInstanceIgnoreNull() {
      return objectMapperIgnoreNull;
   }

   public static ObjectNode objectNode() {
      return getInstance().createObjectNode();
   }

   public static ArrayNode arrayNode() {
      return getInstance().createArrayNode();
   }

   public static boolean isNull(JsonNode jsonNode) {
      return jsonNode == null || jsonNode.isNull() || jsonNode.isMissingNode();
   }

   public static boolean isNullOrEmpty(JsonNode jsonNode) {
      return jsonNode == null || jsonNode.isNull() || jsonNode.isMissingNode() || jsonNode.isEmpty();
   }

   public static String writeValueAsString(Object obj) {
      try {
         return getInstance().writeValueAsString(obj);
      } catch (IOException var2) {
         logger.error(var2.getMessage(), var2);
         return null;
      }
   }

   public static Optional<String> writeValueAsStringOptional(Object obj) {
      return Optional.ofNullable(writeValueAsString(obj));
   }

   public static String writeValueAsStringIgnoreNull(Object obj) {
      try {
         return getInstanceIgnoreNull().writeValueAsString(obj);
      } catch (IOException var2) {
         logger.error(var2.getMessage(), var2);
         return null;
      }
   }

   public static Optional<String> writeValueAsStringIgnoreNullOptional(Object obj) {
      return Optional.ofNullable(writeValueAsStringIgnoreNull(obj));
   }

   public static <T> T readValue(String jsonStr, TypeReference<T> valueTypeRef) {
      try {
         return getInstance().readValue(jsonStr, valueTypeRef);
      } catch (JsonParseException var3) {
         logger.error(var3.getMessage(), var3);
      } catch (JsonMappingException var4) {
         logger.error(var4.getMessage(), var4);
      } catch (IOException var5) {
         logger.error(var5.getMessage(), var5);
      }

      return null;
   }

   public static <T> Optional<T> readValueOptional(String jsonStr, TypeReference<T> valueTypeRef) {
      return Optional.ofNullable(readValue(jsonStr, valueTypeRef));
   }

   public static <T> T readValue(String jsonStr, Class<T> clazz) {
      try {
         return getInstance().readValue(jsonStr, clazz);
      } catch (JsonParseException var3) {
         logger.error(var3.getMessage(), var3);
      } catch (JsonMappingException var4) {
         logger.error(var4.getMessage(), var4);
      } catch (Exception var5) {
         logger.error(var5.getMessage(), var5);
      }

      return null;
   }

   public static <T> Optional<T> readValueOptional(String jsonStr, Class<T> clazz) {
      return Optional.ofNullable(readValue(jsonStr, clazz));
   }

   public static <T> T readValueOrEmpty(String jsonStr, Class<T> clazz) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
      try {
         T t = readValue(jsonStr, clazz);
         return t != null ? t : clazz.getDeclaredConstructor().newInstance();
      } catch (Throwable var3) {
         throw var3;
      }
   }

   public static <T> T readValue(String jsonStr, Class<?> parametrized, Class<?>... parameterClasses) {
      try {
         JavaType javaType = getInstance().getTypeFactory().constructParametricType(parametrized, parameterClasses);
         return getInstance().readValue(jsonStr, javaType);
      } catch (JsonParseException var4) {
         logger.error(var4.getMessage(), var4);
      } catch (JsonMappingException var5) {
         logger.error(var5.getMessage(), var5);
      } catch (IOException var6) {
         logger.error(var6.getMessage(), var6);
      }

      return null;
   }

   public static <T> List<T> readList(String jsonStr, Class<?>... parameterClasses) {
      List<T> list = (List)readValue(jsonStr, List.class, parameterClasses);
      return (List)(list != null ? list : CollUtil.newArrayList(new Object[0]));
   }

   public static JsonNode readTree(String jsonStr) {
      try {
         return getInstance().readTree(jsonStr);
      } catch (IOException var2) {
         logger.error(var2.getMessage(), var2);
         return null;
      }
   }

   public static JsonNode readTreeOrMissing(String jsonStr) {
      JsonNode jsonNode = readTree(jsonStr);
      return jsonNode != null ? jsonNode : getInstance().missingNode();
   }

   public static <T> T treeToValue(JsonNode node, Class<T> clz) {
      try {
         return getInstance().treeToValue(node, clz);
      } catch (IOException var3) {
         logger.error(var3.getMessage(), var3);
         return null;
      }
   }

   public static <T> Optional<T> treeToValueOptional(JsonNode node, Class<T> clz) {
      return Optional.ofNullable(treeToValue(node, clz));
   }

   public static <T> T treeToValueOrEmpty(JsonNode node, Class<T> clz) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
      try {
         T t = treeToValue(node, clz);
         return t != null ? t : clz.getDeclaredConstructor().newInstance();
      } catch (Throwable var3) {
         throw var3;
      }
   }

   public static <T> T treeToValue(JsonNode node, TypeReference<T> typeR) {
      try {
         JavaType javaType = getInstance().getTypeFactory().constructType(typeR);
         return getInstance().treeToValue(node, javaType);
      } catch (IOException var3) {
         logger.error(var3.getMessage(), var3);
         return null;
      }
   }

   public static <T> T treeToValue(JsonNode node, Class<?> parametrized, Class<?>... parameterClasses) {
      try {
         JavaType javaType = getInstance().getTypeFactory().constructParametricType(parametrized, parameterClasses);
         return getInstance().treeToValue(node, javaType);
      } catch (IOException var4) {
         logger.error(var4.getMessage(), var4);
         return null;
      }
   }

   public static <T> Optional<T> treeToValueOptional(JsonNode node, Class<?> parametrized, Class<?>... parameterClasses) {
      return Optional.ofNullable(treeToValue(node, parametrized, parameterClasses));
   }

   public static <T> List<T> treeToList(JsonNode node, Class<T>... parameterClasses) {
      List<T> list = (List)treeToValue(node, List.class, parameterClasses);
      return (List)(list != null ? list : CollUtil.newArrayList(new Object[0]));
   }

   public static JsonNode valueToTree(Object obj) {
      return getInstance().valueToTree(obj);
   }

   public static JsonNode valueToTreeIgnoreNull(Object obj) {
      return getInstanceIgnoreNull().valueToTree(obj);
   }

   public static int getIntValue(JsonNode jsonNode, String key, int defaultValue) {
      if (jsonNode != null && !jsonNode.isNull() && !jsonNode.isMissingNode()) {
         JsonNode node = jsonNode.get(key);
         return node != null && !node.isNull() && !node.isMissingNode() ? node.asInt(defaultValue) : defaultValue;
      } else {
         return defaultValue;
      }
   }

   public static Integer getInt(JsonNode jsonNode, String key) {
      if (jsonNode != null && !jsonNode.isNull() && !jsonNode.isMissingNode()) {
         JsonNode node = jsonNode.get(key);
         return node != null && !node.isNull() && !node.isMissingNode() ? node.asInt() : null;
      } else {
         return null;
      }
   }

   public static long getLongValue(JsonNode jsonNode, String key, long defaultValue) {
      if (jsonNode != null && !jsonNode.isNull() && !jsonNode.isMissingNode()) {
         JsonNode node = jsonNode.get(key);
         return node != null && !node.isNull() && !node.isMissingNode() ? node.asLong(defaultValue) : defaultValue;
      } else {
         return defaultValue;
      }
   }

   public static Long getLong(JsonNode jsonNode, String key) {
      if (jsonNode != null && !jsonNode.isNull() && !jsonNode.isMissingNode()) {
         JsonNode node = jsonNode.get(key);
         return node != null && !node.isNull() && !node.isMissingNode() ? node.asLong() : null;
      } else {
         return null;
      }
   }

   public static double getDoubleValue(JsonNode jsonNode, String key, double defaultValue) {
      if (jsonNode != null && !jsonNode.isNull() && !jsonNode.isMissingNode()) {
         JsonNode node = jsonNode.get(key);
         return node != null && !node.isNull() && !node.isMissingNode() ? node.asDouble(defaultValue) : defaultValue;
      } else {
         return defaultValue;
      }
   }

   public static Double getDouble(JsonNode jsonNode, String key) {
      if (jsonNode != null && !jsonNode.isNull() && !jsonNode.isMissingNode()) {
         JsonNode node = jsonNode.get(key);
         return node != null && !node.isNull() && !node.isMissingNode() ? node.asDouble() : null;
      } else {
         return null;
      }
   }

   public static String getStringValue(JsonNode jsonNode, String key, String defaultValue) {
      return jsonNode.path(key).asText(defaultValue);
   }

   public static String getString(JsonNode jsonNode, String key) {
      return getStringValue(jsonNode, key, (String)null);
   }

   public static JsonNode getByPath(JsonNode node, String expression) {
      if (StringUtils.isEmpty(expression)) {
         return node;
      } else {
         JsonNode result = node;
         String[] var3 = StringUtils.split(expression, ".");
         int var4 = var3.length;

         for(int var5 = 0; var5 < var4; ++var5) {
            String path = var3[var5];
            if (StrUtil.contains(path, "[") && StrUtil.contains(path, "]")) {
               String aryPath = StrUtil.subBefore(path, "[", false);
               int idxPath = NumberUtil.parseInt(StrUtil.subBetween(path, "[", "]"));
               if (idxPath < 0) {
                  idxPath += result.path(aryPath).size();
               }

               result = result.path(aryPath).path(idxPath);
            } else {
               result = result.path(path);
            }
         }

         return result;
      }
   }

   public static <T> T getValueByPath(String jsonStr, String expression, T defaultValue) {
      JsonNode jsonNode = readTreeOrMissing(jsonStr);
      return getValueByPath(jsonNode, expression, defaultValue);
   }

   public static <T> T getValueByPath(JsonNode jsonNode, String expression, T defaultValue) {
      if (jsonNode == null) {
         return defaultValue;
      } else {
         JsonNode nodeByPath = getByPath(jsonNode, expression);
         T value = treeToValue(nodeByPath, new TypeReference<T>() {
         });
         return value != null ? value : defaultValue;
      }
   }

   public static void putByPath(JsonNode node, String expression, Object value) {
      if (node.isObject()) {
         JsonNode currentNode = node;
         Iterator<String> iterator = Arrays.stream(StringUtils.split(expression, ".")).iterator();

         while(true) {
            while(iterator.hasNext()) {
               String path = (String)iterator.next();
               if (StrUtil.contains(path, "[") && StrUtil.contains(path, "]")) {
                  String aryPath = StrUtil.subBefore(path, "[", false);
                  int idxPath = NumberUtil.parseInt(StrUtil.subBetween(path, "[", "]"));
                  if (idxPath < 0) {
                     idxPath += currentNode.path(aryPath).size();
                  }

                  if (!currentNode.path(aryPath).isArray()) {
                     ((ObjectNode)currentNode).putArray(aryPath);
                  }

                  currentNode = currentNode.path(aryPath);
                  if (idxPath >= currentNode.size()) {
                     ((ArrayNode)currentNode).add(objectNode());
                     idxPath = currentNode.size() - 1;
                  }

                  if (!iterator.hasNext()) {
                     if (value instanceof String) {
                        ((ArrayNode)currentNode).set(idxPath, (String)value);
                     } else if (value instanceof JsonNode) {
                        ((ArrayNode)currentNode).set(idxPath, (JsonNode)value);
                     } else {
                        ((ArrayNode)currentNode).set(idxPath, valueToTree(value));
                     }
                  } else {
                     JsonNode nodeByPath = currentNode.path(idxPath);
                     if (nodeByPath.isObject()) {
                        currentNode = nodeByPath;
                     } else {
                        ((ObjectNode)currentNode).putObject(path);
                        currentNode = currentNode.path(path);
                     }
                  }
               } else if (!iterator.hasNext()) {
                  if (value instanceof String) {
                     ((ObjectNode)currentNode).put(path, (String)value);
                  } else if (value instanceof JsonNode) {
                     ((ObjectNode)currentNode).set(path, (JsonNode)value);
                  } else {
                     ((ObjectNode)currentNode).set(path, valueToTree(value));
                  }
               } else {
                  JsonNode nodeByPath = currentNode.path(path);
                  if (nodeByPath.isObject()) {
                     currentNode = nodeByPath;
                  } else {
                     ((ObjectNode)currentNode).putObject(path);
                     currentNode = currentNode.path(path);
                  }
               }
            }

            return;
         }
      }
   }

   public static ArrayNode treeToList(JsonNode param, String childrenColumn, String idColumn, String parentIdColumn, String parentIdValue, String sortColumn) {
      if (param != null && !param.isMissingNode()) {
         ArrayNode result = doTreeToList(param.deepCopy(), childrenColumn, idColumn, parentIdColumn, parentIdValue);
         if (CharSequenceUtil.isNotBlank(sortColumn)) {
            ArrayList<JsonNode> jsonNodes = CollUtil.newArrayList(result.elements());
            jsonNodes.sort((o1, o2) -> {
               String column1 = o1.path(sortColumn).asText();
               String column2 = o2.path(sortColumn).asText();
               return NumberUtil.isNumber(column1) && NumberUtil.isNumber(column2) ? NumberUtil.toBigDecimal(column1).compareTo(NumberUtil.toBigDecimal(column2)) : CharSequenceUtil.compare(column1, column2, true);
            });
            result.removeAll();
            result.addAll(jsonNodes);
         }

         return result;
      } else {
         return arrayNode();
      }
   }

   public static ArrayNode listToTree(JsonNode param, String childrenColumn, String idColumn, String parentIdColumn, String rootParentIdValue) {
      if (param != null && !param.isMissingNode() && !param.isEmpty() && param.isArray()) {
         ArrayNode nodeList = (ArrayNode)param.deepCopy();
         Map<String, JsonNode> nodeMap = (Map)CollUtil.newArrayList(nodeList.elements()).stream().collect(Collectors.toMap((nodex) -> {
            return nodex.path(idColumn).asText();
         }, (nodex) -> {
            return nodex;
         }));
         Iterator elements = nodeList.iterator();

         JsonNode node;
         while(elements.hasNext()) {
            node = (JsonNode)elements.next();
            JsonNode paramNode = (JsonNode)nodeMap.get(node.path(parentIdColumn).asText());
            if (paramNode != null) {
               if (paramNode.get(childrenColumn) == null) {
                  ((ObjectNode)paramNode).putArray(childrenColumn);
               }

               ((ArrayNode)paramNode.get(childrenColumn)).add(node);
            }
         }

         elements = nodeList.elements();

         while(elements.hasNext()) {
            node = (JsonNode)elements.next();
            if (CharSequenceUtil.isBlank(rootParentIdValue)) {
               if (!node.path(parentIdColumn).isMissingNode() && !node.path(parentIdColumn).isNull()) {
                  elements.remove();
               }
            } else if (!rootParentIdValue.equals(node.path(parentIdColumn).asText())) {
               elements.remove();
            }
         }

         return nodeList;
      } else {
         return arrayNode();
      }
   }

   private static ArrayNode doTreeToList(JsonNode param, String childrenColumn, String idColumn, String parentIdColumn, String parentIdValue) {
      ArrayNode result = arrayNode();
      if (param.isArray()) {
         Iterator var6 = param.iterator();

         while(var6.hasNext()) {
            JsonNode node = (JsonNode)var6.next();
            result.addAll(doTreeToList(node, childrenColumn, idColumn, parentIdColumn, parentIdValue));
         }
      } else if (param.isObject()) {
         ObjectNode node = (ObjectNode)param;
         String nodeId = node.path(idColumn).asText();
         JsonNode children = node.get(childrenColumn);
         if (children != null && !children.isMissingNode()) {
            result.addAll(doTreeToList(children, childrenColumn, idColumn, parentIdColumn, nodeId));
         }

         if (node.path(parentIdColumn).isMissingNode()) {
            node.put(parentIdColumn, parentIdValue);
         }

         node.remove(childrenColumn);
         result.add(node);
      }

      return result;
   }

   static {
      objectMapper = (new ObjectMapper()).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).registerModule((new SimpleModule()).addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))).addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss"))).addSerializer(Long.class, new JsonSerializer<Long>() {
         public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString(String.valueOf(value));
         }
      }).addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))).addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss"))));
      objectMapperIgnoreNull = (new ObjectMapper()).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).setSerializationInclusion(Include.NON_NULL).registerModule((new SimpleModule()).addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))).addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss"))).addSerializer(Long.class, new JsonSerializer<Long>() {
         public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString(String.valueOf(value));
         }
      }).addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))).addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss"))));
      logger = LoggerFactory.getLogger(JacksonUtil.class);
   }
}
