package com.njzscloud.common.core.jackson.serializer;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.ienum.Dict;
import com.njzscloud.common.core.ienum.DictInt;
import com.njzscloud.common.core.ienum.DictStr;
import com.njzscloud.common.core.ienum.IEnum;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.Field;

/**
 * Dict 枚举的 Jackson 反序列化器<br/><br/>
 * JSON 格式见对应的序列化器 {@link DictSerializer}
 *
 * @see Dict
 * @see DictInt
 * @see DictStr
 * @see DictSerializer
 */
@Slf4j
public class DictDeserializer extends JsonDeserializer<Dict> {
    private static final ClassLoader CLASSLOADER = DictDeserializer.class.getClassLoader();

    @Override
    public Dict deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
        JsonToken currentToken = p.getCurrentToken();

        if (currentToken == JsonToken.START_OBJECT) {
            TreeNode treeNode = p.getCodec().readTree(p);
            TreeNode enumType_node = treeNode.get(IEnum.ENUM_TYPE);
            String enumTypeField = ((TextNode) enumType_node).textValue();
            TreeNode val_node = treeNode.get(Dict.ENUM_VAL);

            Class<?> clazz;
            try {
                clazz = CLASSLOADER.loadClass(enumTypeField);
            } catch (ClassNotFoundException e) {
                throw Exceptions.error(e, "类型加载失败：{}", enumTypeField);
            }
            if (val_node instanceof TextNode) {
                if (DictStr.class.isAssignableFrom(clazz)) {
                    String val = ((TextNode) val_node).textValue();
                    DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
                    return Dict.parse(val, constants);
                } else if (DictInt.class.isAssignableFrom(clazz)) {
                    String val = ((TextNode) val_node).textValue();
                    DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
                    return Dict.parse(Integer.parseInt(val), constants);
                } else {
                    return null;
                }
            } else if (val_node instanceof IntNode) {
                if (DictStr.class.isAssignableFrom(clazz)) {
                    int val = ((IntNode) val_node).intValue();
                    DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
                    return Dict.parse(String.valueOf(val), constants);
                } else if (DictInt.class.isAssignableFrom(clazz)) {
                    int val = ((IntNode) val_node).intValue();
                    DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
                    return Dict.parse(val, constants);
                } else {
                    return null;
                }
            } else {
                return null;
            }

        } else {
            JsonStreamContext context = p.getParsingContext();
            String currentName = context.getCurrentName();

            Object currentValue = p.getCurrentValue();

            Class<?> valueClazz = currentValue.getClass();
            try {
                Field field = valueClazz.getDeclaredField(currentName);
                Class<?> clazz = field.getType();
                if (DictStr.class.isAssignableFrom(clazz)) {
                    String val = p.getValueAsString();
                    DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
                    return Dict.parse(val, constants);
                } else if (DictInt.class.isAssignableFrom(clazz)) {
                    int val = p.getValueAsInt();
                    DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
                    return Dict.parse(val, constants);
                } else {
                    return null;
                }
            } catch (Exception e) {
                log.error("字典枚举反序列化失败", e);
                throw Exceptions.error(e, "字典枚举反序列化失败，字段名：{}，值：{}", currentName, currentValue);
            }
        }
    }
}
