package com.face.util;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.google.common.collect.Sets;
import com.face.global.enums.CommonCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Set;

@Slf4j
public class JsonUtils {
    @Getter
    private static ObjectMapper OBJECT_MAPPER;

    private static final Set<String> FORMAT_DATES = Sets.newHashSet("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd");


    public static class DateJsonDeserializer extends JsonDeserializer<Date> {
        @Override
        public Date deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
            String valueStr = p.getValueAsString();
            if (StringUtils.isBlank(valueStr)) {
                return null;
            }
            if (NumberUtils.isCreatable(valueStr)) {
                return new Date(Long.parseLong(valueStr));
            }
            for (String format : FORMAT_DATES) {
                try {
                    return new SimpleDateFormat(format).parse(valueStr);
                } catch (Exception e) {
                    // ignore
                }
            }
            throw new IllegalArgumentException(valueStr.concat(" convert date fail"));
        }
    }

    static {
        OBJECT_MAPPER = JsonMapper.builder()
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true)
                .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
                .build();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addDeserializer(Date.class, new DateJsonDeserializer());
        OBJECT_MAPPER.registerModule(simpleModule);
    }


    public static String toJsonString(Object value) {
        return toJsonString(value, false);
    }

    public static String toJsonString(Object value, boolean throwException) {
        try {
            return OBJECT_MAPPER.writeValueAsString(value);
        } catch (Exception e) {
            log.error("toJsonStringFail", e);
            throwBusinessException(throwException, e);
        }
        return null;
    }

    public static <T> T parseObject(String value, TypeReference<T> typeReference) {
        return parseObject(value, typeReference, false);
    }

    public static <T> T parseObject(String value, JavaType javaType) {
        return parseObject(value, javaType, false);
    }

    public static <T> T parseObject(String value, JavaType javaType, boolean throwException) {
        try {
            return OBJECT_MAPPER.readValue(value, javaType);
        } catch (Exception e) {
            log.error("parseObjectFail", e);
            throwBusinessException(throwException, e);
        }
        return null;
    }

    public static <T> T parseObject(String value, TypeReference<T> typeReference, boolean throwException) {
        try {
            return OBJECT_MAPPER.readValue(value, typeReference);
        } catch (Exception e) {
            log.error("parseObjectFail", e);
            throwBusinessException(throwException, e);
        }
        return null;
    }

    public static <T> T parseObject(String value, Class<T> cls) {
        return parseObject(value, cls, false);
    }

    public static <T> T parseObject(String value, Class<T> cls, boolean throwException) {
        try {
            return OBJECT_MAPPER.readValue(value, cls);
        } catch (Exception e) {
            log.error("parseObjectFail", e);
            throwBusinessException(throwException, e);
        }
        return null;
    }


    private static void throwBusinessException(boolean throwException, Exception exception) {
        if (throwException) {
            throw SystemExceptionUtils.buildBusinessException(CommonCode.ERROR.getCode(), new Object[]{}, exception);
        }
    }

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