package cc.smtweb.framework.core.util;

import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.framework.core.exception.JsonParseException;
import cc.smtweb.framework.core.util.jackson.*;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.*;

import static org.springframework.beans.BeanUtils.getPropertyDescriptors;

/**
 * json工具类
 */
@Slf4j
public class JsonUtil {
    public final static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    public final static ObjectMapper API_OBJECT_MAPPER = new ObjectMapper();

    static {
        init(OBJECT_MAPPER);
        // 不序列化空值
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//    OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
//    OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
//        // 设置将MAP转换为JSON时候只转换值不等于NULL的
//        objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);

        init(API_OBJECT_MAPPER);
        API_OBJECT_MAPPER.getSerializerProvider().setNullValueSerializer(new NullSerializer());

        FilterProvider filters = new SimpleFilterProvider().addFilter("apiFilter", SimpleBeanPropertyFilter.serializeAllExcept(new HashSet<>()));
    }

    private static void init(ObjectMapper mapper) {
        // JSON转化为对象时忽略未对应的属性
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        SimpleModule module = new SimpleModule();
        LongSerializer longSerializer = new LongSerializer();
        module.addSerializer(Long.class, longSerializer);
        module.addSerializer(Long.TYPE, longSerializer);

        DateSerializer dateSerializer = new DateSerializer();
        module.addSerializer(java.sql.Date.class, dateSerializer);
        TimeSerializer timeSerializer = new TimeSerializer();
        module.addSerializer(java.sql.Time.class, timeSerializer);
//        module.addSerializer(BaseBean.class, new BaseBeanSerializer());

        module.addDeserializer(Timestamp.class, new TimestampDeserializer());
        module.addDeserializer(java.util.Date.class, new DateDeserializer());

        mapper.registerModule(module);
        mapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
    }

    public static JsonNode readTree(String body) {
        try {
            return OBJECT_MAPPER.readTree(body);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }

        return null;
    }

    // 创建新节点
    public static ObjectNode createObjectNode() {
        return OBJECT_MAPPER.createObjectNode();
    }

    // 将 JsonNode 对象转成 json
    public static String writeValueAsString(JsonNode newNode) {
        try {
            return OBJECT_MAPPER.writeValueAsString(newNode);
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(), e);
        }

        return null;
    }

    public static <T> T parse(String str, Class<T> clazz) {
        try {

            if (StringUtils.isBlank(str)) {
                return null;
            } else {
                return OBJECT_MAPPER.readValue(str, clazz);
            }
        } catch (Exception e) {
            throw new JsonParseException("can't convert this json to " + clazz + " type", e);
        }
    }

    /**
     * 解析指定json节点
     *
     * @param str      完整json串
     * @param nodePath 待解析节点路径，如a.b.c
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T parse(String str, String nodePath, Class<T> clazz) {
        try {
            if (StringUtils.isBlank(str)) {
                return null;
            }
            JsonNode node = OBJECT_MAPPER.readTree(str);
            String[] nodes = nodePath.split("\\.");
            for (String s : nodes) {
                node = node.get(s);
                if (node == null) return null;
            }
            return OBJECT_MAPPER.treeToValue(node, clazz);
        } catch (Exception e) {
            throw new JsonParseException("can't convert this json to " + clazz + " type", e);
        }
    }

    /**
     * 将Map对象转换为Bean对象
     *
     * @param map   Map对象
     * @param clazz Bean对象类
     * @param <T>   Bean对象类型
     * @return Bean对象实例
     */
    public static <T> T parse(Map map, Class<T> clazz) {
        return parse(encodeString(map), clazz);
    }

    //此方法仅能解析简单属性，准备废弃
    @Deprecated
    public static <T> T parseSimple(Map map, Class<T> clazz) {
        try {
            if (map == null || map.isEmpty()) {
                return null;
            } else {
                // 对象实例化
                T bean = BeanUtils.instantiateClass(clazz);
                if (SwMap.class.isAssignableFrom(clazz)) {
                    ((SwMap) bean).putAll(map);
                } else {
                    // 循环设置对象属性
                    PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(clazz);
                    for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                        String properName = propertyDescriptor.getName();
                        // 过滤class属性
                        if (!"class".equals(properName)) {
                            if (map.containsKey(properName)) {
                                Method writeMethod = propertyDescriptor.getWriteMethod();
                                if (writeMethod != null) {
                                    Object value = map.get(properName);
                                    if (!writeMethod.isAccessible()) {
                                        writeMethod.setAccessible(true);
                                    }
                                    writeMethod.invoke(bean, value);
                                }
                            }
                        }
                    }
                }

                return bean;
            }
        } catch (Exception e) {
            throw new JsonParseException("can't convert map to " + clazz + " type", e);
        }
    }

    public static <T> T parse(InputStream is, Class<T> clazz) {
        try {
            if (is == null) {
                return null;
            } else {
                return OBJECT_MAPPER.readValue(is, clazz);
            }
        } catch (Exception e) {
            throw new JsonParseException("can't convert this json to " + clazz + " type", e);
        }
    }

    public static <T> T parse(byte[] str, Class<T> clazz) {
        try {
            if (str == null || str.length == 0) {
                return null;
            } else {
                return OBJECT_MAPPER.readValue(str, clazz);
            }
        } catch (Exception e) {
            throw new JsonParseException("can't convert this json to " + clazz + " type", e);
        }
    }

    public static JsonNode parse(String str) {
        try {
            if (StringUtils.isBlank(str)) {
                return null;
            } else {
                return OBJECT_MAPPER.readTree(str);
            }
        } catch (Exception e) {
            throw new JsonParseException("can't convert this json to JsonNode", e);
        }
    }

    /**
     * 转换json文本为对象列表
     *
     * @param is    json文本流
     * @param <T>   列表中对象类
     * @param clazz 列表中的对象类
     * @return 列表对象
     */
    public static <T> List<T> parseList(InputStream is, Class<T> clazz) {
        try {
            if (is == null) {
                return null;
            } else {
                JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(ArrayList.class, clazz);
                return OBJECT_MAPPER.readValue(is, javaType);
            }
        } catch (Exception e) {
            throw new JsonParseException("can't convert this json to list<T> type", e);
        }
    }

    /**
     * 转换json文本为对象列表
     *
     * @param str   json文本字节数组
     * @param <T>   列表中对象类
     * @param clazz 列表中的对象类
     * @return 列表对象
     */
    public static <T> List<T> parseList(byte[] str, Class<T> clazz) {
        try {
            if (str == null || str.length == 0) {
                return null;
            } else {
                JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(ArrayList.class, clazz);
                return OBJECT_MAPPER.readValue(str, javaType);
            }
        } catch (Exception e) {
            throw new JsonParseException("can't convert this json to list<T> type", e);
        }
    }

    /**
     * 转换json文本为对象列表
     *
     * @param str   json文本
     * @param <T>   列表中对象类
     * @param clazz 列表中的对象类
     * @return 列表对象
     */
    public static <T> List<T> parseList(String str, Class<T> clazz) {
        try {
            if (StringUtils.isBlank(str)) {
                return null;
            } else {
                JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(ArrayList.class, clazz);
                return OBJECT_MAPPER.readValue(str, javaType);
            }
        } catch (Exception e) {
            throw new JsonParseException("can't convert this json to list<" + clazz.getSimpleName() + "> type", e);
        }
    }

    public static Map parseMap(String str) {
        try {
            if (StringUtils.isBlank(str)) {
                return null;
            } else {
                return OBJECT_MAPPER.readValue(str, Map.class);
            }
        } catch (Exception e) {
            throw new JsonParseException("can't convert this json to Map type", e);
        }
    }

    public static String encodeString(Object obj) {
        try {
            return OBJECT_MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new JsonParseException("can't convert type " + obj.getClass() + " to json string", e);
        }
    }

    public static byte[] encodeBytes(Object obj) {
        try {
            return OBJECT_MAPPER.writeValueAsBytes(obj);
        } catch (JsonProcessingException e) {
            throw new JsonParseException("can't convert type " + obj.getClass() + " to json string", e);
        }
    }

    public static SwMap bean2Map(Object obj) {
        return parse(encodeString(obj), SwMap.class);
    }

    public static List<SwMap> bean2MapList(Object obj) {
        List<SwMap> list = new ArrayList<>();
        SwMap[] arr = parse(encodeString(obj), SwMap[].class);
        Collections.addAll(list, arr);
        return list;
    }
}
