package pieces100

import alraune.*
import bolone.test.Test_Wilma
import com.fasterxml.jackson.annotation.JsonFilter
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.jsontype.TypeSerializer
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.ser.PropertyWriter
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider
import com.fasterxml.jackson.databind.ser.std.StdSerializer
import vgrechka.*
import java.sql.Timestamp
import kotlin.reflect.KClass
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.memberProperties

@TestRefs(Test_Wilma::class)
object Wilma {
    val typeProperty = "@type"

    private val insecureMapper = ObjectMapper().also {
        it.setDefaultTyping(object : ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL) {
            init {
                init(JsonTypeInfo.Id.CLASS, null)
                inclusion(JsonTypeInfo.As.PROPERTY)
                typeProperty(this@Wilma.typeProperty)
            }

            override fun useForType(t: JavaType): Boolean {
                if (t.isContainerType) return false
                if (t.rawClass == java.lang.Long::class.java) return false
                if (t.rawClass == JSLong::class.java) return false
                if (t.rawClass == Timestamp::class.java) return false
                if (t.isEnumType) return false
                return true
            }
        })

        it.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)

        it.registerModule(SimpleModule().also {
            it.addSerializer(java.lang.Long::class.java, object : StdSerializer<java.lang.Long>(java.lang.Long::class.java) {
                override fun serialize(value: java.lang.Long, gen: JsonGenerator, provider: SerializerProvider) {
                    fart(gen, value)
                }
                override fun serializeWithType(value: java.lang.Long, gen: JsonGenerator, serializers: SerializerProvider, typeSer: TypeSerializer) {
                    fart(gen, value)
                }
                private fun fart(gen: JsonGenerator, value: java.lang.Long) {
                    gen.writeString(value.toString())
                }
            })

            it.addSerializer(Long::class.java, object : StdSerializer<Long>(Long::class.java) {
                override fun serialize(value: Long, gen: JsonGenerator, provider: SerializerProvider) {
                    fart(gen, value)
                }
                override fun serializeWithType(value: Long, gen: JsonGenerator, serializers: SerializerProvider, typeSer: TypeSerializer) {
                    return fart(gen, value)
                }
                private fun fart(gen: JsonGenerator, value: Long) {
                    gen.writeString(value.toString())
                }
            })

            it.addSerializer(JSLong::class.java, object : StdSerializer<JSLong>(JSLong::class.java) {
                override fun serialize(value: JSLong, gen: JsonGenerator, provider: SerializerProvider) {
                    fart(gen, value)
                }
                override fun serializeWithType(value: JSLong, gen: JsonGenerator, serializers: SerializerProvider, typeSer: TypeSerializer) {
                    return fart(gen, value)
                }
                private fun fart(gen: JsonGenerator, value: JSLong) {
                    gen.writeNumber(value.value)
                }
            })

            it.addSerializer(Timestamp::class.java, object : StdSerializer<Timestamp>(Timestamp::class.java) {
                override fun serialize(value: Timestamp, gen: JsonGenerator, provider: SerializerProvider) {
                    fart(gen, value)
                }
                override fun serializeWithType(value: Timestamp, gen: JsonGenerator, serializers: SerializerProvider, typeSer: TypeSerializer) {
                    fart(gen, value)
                }
                private fun fart(gen: JsonGenerator, value: Timestamp) {
                    checkJSLong(value.time)
                    gen.writeStartObject()
                    gen.writeStringField(typeProperty, "alraunets.Timestamp")
                    gen.writeNumberField("time", value.time)
                    gen.writeEndObject()
                }
            })

            it.addSerializer(Enum::class.java, object : StdSerializer<Enum<*>>(Enum::class.java) {
                override fun serialize(value: Enum<*>, gen: JsonGenerator, provider: SerializerProvider) {
                    fart(gen, value)
                }

                override fun serializeWithType(value: Enum<*>, gen: JsonGenerator, serializers: SerializerProvider, typeSer: TypeSerializer) {
                    fart(gen, value)
                }
                private fun fart(gen: JsonGenerator, value: Enum<*>) {
                    gen.writeString(value.name)
                }
            })
        })

        @JsonFilter("6abd3b2c-1a93-4358-a43a-a0c4e9bc8eea")
        class Mixin

        it.addMixIn(java.lang.Object::class.java, Mixin::class.java)
    }

    val filteredWriter = insecureMapper.writer(SimpleFilterProvider().also {
        it.addFilter("6abd3b2c-1a93-4358-a43a-a0c4e9bc8eea", object : SimpleBeanPropertyFilter() {
            override fun serializeAsField(pojo: Any?, jgen: JsonGenerator, provider: SerializerProvider, writer: PropertyWriter) {
                val member = writer.member
                if (member != null) { // There is no members when writing, for example, Map
                    val prop = member.declaringClass.kotlin.memberProperties.first {it.name == writer.name}
                    if (prop.findAnnotation<AdminField>() != null)
                        if (!isAdmin())
                            return
                }

                super.serializeAsField(pojo, jgen, provider, writer)
            }
        })
    })

    fun <T : Any> readValue(json: String, cls: KClass<T>): T = insecureMapper.readValue(json, cls.java)!!

    fun writeValue(x: Any): String = filteredWriter.writeValueAsString(x)

    fun writeValuePretty(x: Any): String = filteredWriter.withDefaultPrettyPrinter().writeValueAsString(x)
}



