package xtools.boot.web.base.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonGenerator;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.BeanProperty;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.SerializationContext;
import tools.jackson.databind.ValueDeserializer;
import tools.jackson.databind.ValueSerializer;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.module.SimpleModule;
import xtools.boot.api.anntation.IgnoreXss;
import xtools.boot.core.utils.TimeUtils;
import xtools.core.time.InstantUtils;
import xtools.web.xss.XssUtils;

import java.time.Instant;
import java.util.Objects;

/**
 * <p>Title : JacksonConfig</p>
 * <p>Description : JacksonConfig</p>
 * <p>DevelopTools : Idea_x64_v2026.1</p>
 * <p>DevelopSystem : macOS Sequoia 15.7.5</p>
 * <p>Company : org.xujun</p>
 *
 * @author : XuJun
 * @version : 5.0.0
 * @date : 2026/1/6 12:15
 */
@Configuration
public class JacksonConfig {

    /**
     * 忽略 null 值
     */
    public static final JsonMapper NON_NULL;

    // 初始化
    static {
        NON_NULL = JsonMapper.builder().changeDefaultPropertyInclusion(handler -> handler.withValueInclusion(JsonInclude.Include.NON_NULL)).build();
    }

    /**
     * 创建 SimpleModule
     *
     * @return SimpleModule
     */
    @Bean
    public SimpleModule simpleModule() {
        SimpleModule module = new SimpleModule();

        // 序列化
        module.addSerializer(Long.class, new ValueSerializer<>() {
            @Override
            public void serialize(Long value, JsonGenerator gen, SerializationContext context) throws JacksonException {
                if (value == null) {
                    gen.writeNull();
                } else {
                    gen.writeString(value.toString());
                }
            }
        });
        module.addSerializer(Instant.class, new ValueSerializer<>() {
            @Override
            public void serialize(Instant value, JsonGenerator gen, SerializationContext context) throws JacksonException {
                if (value == null) {
                    gen.writeNull();
                } else {
                    gen.writeString(InstantUtils.format(value));
                }
            }
        });

        // 反序列化
        module.addDeserializer(Instant.class, new ValueDeserializer<>() {
            @Override
            public Instant deserialize(JsonParser p, DeserializationContext context) throws JacksonException {
                return TimeUtils.toInstant(p.getString());
            }
        });
        // xss处理
        module.addDeserializer(String.class, new XssStringDeserializer());

        return module;
    }

    /**
     * XSS 字符串反序列化器
     */
    static class XssStringDeserializer extends ValueDeserializer<String> {

        /**
         * 是否忽略 XSS 过滤
         */
        private final boolean ignore;

        /**
         * 创建 XSS 忽略字符串反序列化器
         */
        XssStringDeserializer() {
            this(false);
        }

        /**
         * 创建 XSS 忽略字符串反序列化器
         *
         * @param ignore 是否忽略 XSS 过滤
         */
        XssStringDeserializer(boolean ignore) {
            this.ignore = ignore;
        }

        @Override
        public String deserialize(JsonParser parser, DeserializationContext context) throws JacksonException {
            String value = parser.getString();
            if (ignore) {
                return value;
            }
            return XssUtils.filterParam(value);
        }

        @Override
        public ValueDeserializer<String> createContextual(DeserializationContext context, BeanProperty property) throws JacksonException {
            if (Objects.nonNull(property) && Objects.nonNull(property.getAnnotation(IgnoreXss.class))) {
                return new XssStringDeserializer(true);
            }
            return new XssStringDeserializer(false);
        }
    }
}
