package com.weiqi.mis;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.google.common.base.Preconditions;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

@Slf4j
public class JacksonHelper {

    private static ObjectMapper mapper = new ObjectMapper();

    static {
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
        javaTimeModule.addDeserializer(LocalDateTime.class,
                new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
        javaTimeModule.addSerializer(Date.class,
                new DateSerializer(false, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addDeserializer(Date.class, new CustomDateDeserializer());

        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
              .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
              .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
              .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
              .registerModule(new ParameterNamesModule())
              .registerModule(javaTimeModule);
        ;

        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
    }

    private JacksonHelper() {
    }

    public static ObjectMapper getMapper() {
        return mapper;
    }

    @SneakyThrows
    public static String serialize(Object o) {
        return mapper.writeValueAsString(o);
    }

    @SneakyThrows
    public static String serialize(Object o, boolean pretty) {
        if (pretty) {
            return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(o);
        }
        return mapper.writeValueAsString(o);
    }


    @SneakyThrows
    public static <T> T deserialize(String s, Class<T> clazz) {
        if (s == null || s.length() == 0) {
            return null;
        }


        T t = mapper.readValue(s, clazz);
        return t;

    }

    @SneakyThrows
    public static <T> T deserialize(String s, JavaType javaType) {
        if (s == null || s.length() == 0) {
            return null;
        }

        T t = mapper.readValue(s, javaType);
        return t;
    }

    @SneakyThrows
    public static <T> T deserialize(String s, TypeReference<T> clazz) {

        if (s == null || s.length() == 0) {
            return null;
        }

        T t = (T) mapper.readValue(s, clazz);
        return t;
    }

    public static <T> T deserialize(String s, Class<T> parametrized, Class<?>... parameterClasses) {
        if (s == null || s.length() == 0) {
            return null;
        }

        try {
            if (parameterClasses == null || parameterClasses.length == 0) {
                T t = mapper.readValue(s, parametrized);
            }
            T t = mapper.readValue(s,
                    TypeFactory.defaultInstance().constructParametricType(parametrized, parameterClasses));
            return t;
        } catch (IOException e) {
            log.error(s + ", " + e.getMessage());
        }
        return null;
    }

    @SneakyThrows
    public static JsonNode deserialize(String s) {
        if (s == null || s.length() == 0) {
            return null;
        }

        JsonNode t = mapper.readTree(s);
        return t;
    }

    /**
     * Description   将json数据按规则组装成List<Map>格式
     *
     * @param jsonList 列表json数据
     * @param map      对应规则 old -> new
     * @param flag     是否允许旧字段值为空
     * @return
     */
    @SneakyThrows
    public static List<Map<String, String>> reloadData(String jsonList, Map<String, String> map, boolean flag) {
        List<Map<String, String>> dataMaps = new ArrayList<>();
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jsonNodes = objectMapper.readTree(jsonList);
        Iterator<JsonNode> jsonNodeIterator = jsonNodes.iterator();
        boolean isList = jsonNodes.isArray();
        if (isList) {
            while (jsonNodeIterator.hasNext()) {
                Map<String, String> item = new HashMap();
                JsonNode jsonNode = jsonNodeIterator.next();

                //获取新旧字段对应规则
                Set<Map.Entry<String, String>> entries = map.entrySet();
                for (Map.Entry<String, String> entry : entries) {
                    JsonNode jsonNodeItem = jsonNode;
                    String oldFiled = entry.getKey();
                    String newFiled = entry.getValue();
                    Preconditions.checkArgument(StringUtils.isNotEmpty(oldFiled), "oldFiled must not be empty");
                    Preconditions.checkArgument(StringUtils.isNotEmpty(newFiled), "newFiled must not be empty");

                    String[] split = oldFiled.split("-");
                    if (split.length >= 2) {
                        for (int i = 0; i < split.length - 1; i++) {
                            jsonNodeItem = jsonNodeItem.get(split[i]);
                            if (i == split.length - 2) {
                                oldFiled = split[split.length - 1];
                            }
                        }
                    }

                    //旧字段值
                    String oldValue = null;
                    try {
                        oldValue = jsonNodeItem.get(oldFiled).asText();
                    } catch (Exception e) {
                        if (!flag) {
                            log.error(oldFiled + " :  get oldValue is empty" + jsonNodes.toString());
                        }
                    } finally {
                        item.put(newFiled, oldValue);
                    }
                    if (!flag) {
                        Preconditions.checkArgument(StringUtils.isNotEmpty(oldValue), "oldValue must not be empty");
                    }
                }
                if (item.size() == entries.size()) {
                    dataMaps.add(item);
                }
            }
        } else {
            Map<String, String> item = new HashMap();
            //获取新旧字段对应规则
            Set<Map.Entry<String, String>> entries = map.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                JsonNode jsonNodeItem = jsonNodes;
                String oldFiled = entry.getKey();
                String newFiled = entry.getValue();
                Preconditions.checkArgument(StringUtils.isNotEmpty(oldFiled), "oldFiled must not be empty");
                Preconditions.checkArgument(StringUtils.isNotEmpty(newFiled), "newFiled must not be empty");
                //旧字段值

                String[] split = oldFiled.split("-");
                if (split.length >= 2) {
                    for (int i = 0; i < split.length - 1; i++) {
                        jsonNodeItem = jsonNodeItem.get(split[i]);
                        if (i == split.length - 2) {
                            oldFiled = split[split.length - 1];
                        }
                    }
                }

                String oldValue = null;
                try {
                    oldValue = jsonNodeItem.get(oldFiled).asText();
                } catch (Exception e) {
                    if (!flag) {
                        log.error(oldFiled + " :  get oldValue is empty" + jsonNodes.toString());
                    }
                } finally {
                    item.put(newFiled, oldValue);
                }
                if (!flag) {
                    Preconditions.checkArgument(StringUtils.isNotEmpty(oldValue), "oldValue must not be empty");
                }
            }
            if (item.size() == entries.size()) {
                dataMaps.add(item);
            }
        }
        return dataMaps;
    }

    public static TypeFactory getTypeFactory() {
        return mapper.getTypeFactory();
    }

    public static <T> T toObject(JsonNode node, Class<T> clazz) {
        if (node == null) {
            return null;
        }

        if (node.isArray()) {
            log.error("error when converting jsonNode to object. node is array. node: " + node);
            return null;
        }

        T t = null;
        try {
            t = mapper.treeToValue(node, clazz);
        } catch (Exception ex) {
            log.error("error when converting jsonNode to object. node: " + node, ex);
        }

        return t;
    }

    static class CustomDateDeserializer extends DateDeserializers.DateDeserializer {


        @Override
        public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {

            Date date = null;

            try {
                date = super.deserialize(p, ctxt);
            } catch (IOException e) {
            }


            if (date == null) {
                date = DateHelper.deserialize(p.getText());
            }

            return date;
        }
    }


}
