package com.adealink.weparty.module.widget.form.data

import android.os.Parcelable
import com.adealink.frame.base.Rlt
import com.adealink.frame.data.json.froJsonErrorNull
import com.adealink.frame.data.json.toJsonErrorNull
import com.adealink.frame.util.getJSONObject
import com.adealink.weparty.util.formatNumberStr
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import com.google.gson.annotations.GsonNullable
import com.google.gson.annotations.JsonAdapter
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
import java.lang.reflect.Type

@JsonAdapter(StepFieldParser::class)
@Parcelize
sealed class StepField<T>(
    @SerializedName("fieldCode") val fieldCode: String = "", //字段编码
    @SerializedName("fieldName") val fieldName: String = "", //字段名称
    @SerializedName("fieldDesc") val fieldDesc: String = "", //字段描述
    @SerializedName("type") val fieldType: Int = 0, //0:单行文本，1:多行文本，2:单选，3:多选，4:数字，5:图片，6:日期，7:语音，8:视频
    @GsonNullable
    @SerializedName("duration") val duration: Int? = null, //时长秒，type类型时语音/视频时有效
    @GsonNullable
    @SerializedName("constants") val constants: List<StepFieldConstant>? = null, //当type=2/3时，可读取这个常量列表作为选择项
    @GsonNullable
    @SerializedName("validate") val validate: StepFieldValidate? = null, //校验器
    @GsonNullable
    @SerializedName("example") val example: StepFieldExample? = null, //示例
    @GsonNullable
    @SerializedName("icon") val icon: String? = null, //图标
) : Parcelable {

    open fun validate(inputValue: T?): Rlt<Any> {
        return Rlt.Success(Unit)
    }

    fun isPriceField(): Boolean {
        return fieldCode == FIELD_CODE_PRICE
    }

    fun isFieldNeedReview(): Boolean {
        //是否需要审核(价格, 单位不需要审核)
        return fieldCode != FIELD_CODE_PRICE
                && fieldCode != FIELD_CODE_UNIT
    }

    open fun getFieldValueDesc(): String? {
        return null
    }
}


enum class StepFieldValueType(val valueType: Int, val array: Boolean, val clazz: Class<*>?) {
    STRING(0, false, StringStepField::class.java),
    STRING_ARRAY(0, true, StringArrayStepField::class.java),
    INTEGER(1, false, IntegerStepField::class.java),
    INTEGER_ARRAY(1, true, IntegerArrayStepField::class.java),
    DOUBLE(2, false, DoubleStepField::class.java),
    DOUBLE_ARRAY(2, true, DoubleArrayStepField::class.java);

    companion object {

        @JvmStatic
        fun getValueType(valueType: Int, array: Boolean): StepFieldValueType? {
            return entries.find {
                it.valueType == valueType && it.array == array
            }
        }

        @JvmStatic
        fun getValueTypeClass(valueType: Int, array: Boolean): Class<*>? {
            return getValueType(valueType, array)?.clazz
        }
    }

}

private const val FIELD_ARRAY = "array"
private const val FIELD_VALUE_TYPE = "valueClassType"

object StepFieldParser : JsonDeserializer<StepField<*>>, JsonSerializer<StepField<*>> {
    override fun deserialize(
        json: JsonElement?,
        typeOfT: Type?,
        context: JsonDeserializationContext?
    ): StepField<*>? {
        val obj = json?.toString() ?: return null
        val jsonObject = obj.getJSONObject()
        val valueType = jsonObject?.optInt(FIELD_VALUE_TYPE) ?: return null
        val isArray = jsonObject.optBoolean(FIELD_ARRAY)
        val clazz = StepFieldValueType.getValueTypeClass(valueType, isArray) ?: return null
        return froJsonErrorNull(obj, clazz as Type)
    }

    override fun serialize(
        src: StepField<*>?,
        typeOfSrc: Type?,
        context: JsonSerializationContext?
    ): JsonElement? {
        return if (src == null) null else JsonPrimitive(toJsonErrorNull(src))
    }
}

data class StringStepField(
    @GsonNullable
    @SerializedName("value") val fieldValue: String?,
) : StepField<String>() {
    override fun validate(inputValue: String?): Rlt<Any> {
        if (validate?.required == null) {
            return Rlt.Success(Unit)
        }
        if (validate.required) {
            if (inputValue.isNullOrEmpty()) {
                return Rlt.Failed(RequireValueNullError())
            }
        }
        if (validate.size != null && validate.size.isValid()) {
            val inputLength = inputValue?.length ?: 0
            if (inputLength < validate.size.min) {
                return Rlt.Failed(StringLessThanMinError())
            }
            if (inputLength > validate.size.max) {
                return Rlt.Failed(StringMoreThanMaxError())
            }
        }
        return Rlt.Success(Unit)
    }

    override fun getFieldValueDesc(): String? {
        return fieldValue
    }
}

data class StringArrayStepField(
    @GsonNullable
    @SerializedName("value") val fieldValue: List<String>?,
) : StepField<List<String>>() {
    override fun validate(inputValue: List<String>?): Rlt<Any> {
        if (validate?.required == null) {
            return Rlt.Success(Unit)
        }
        if (validate.required) {
            if (inputValue.isNullOrEmpty()) {
                return Rlt.Failed(RequireValueNullError())
            }
        }
        if (validate.arraySize != null && validate.arraySize.isValid()) {
            val inputLength = inputValue?.size ?: 0
            if (inputLength < validate.arraySize.min) {
                return Rlt.Failed(ArrayLessThanMinError())
            }
            if (inputLength > validate.arraySize.max) {
                return Rlt.Failed(ArrayMoreThanMaxError())
            }
        }

        if (inputValue != null && validate.size != null && validate.size.isValid()) {
            for (input in inputValue) {
                val inputLength = input.length
                if (inputLength < validate.size.min) {
                    return Rlt.Failed(StringLessThanMinError())
                }
                if (inputLength > validate.size.max) {
                    return Rlt.Failed(StringMoreThanMaxError())
                }
            }
        }

        return Rlt.Success(Unit)
    }

    override fun getFieldValueDesc(): String? {
        return fieldValue?.joinToString(separator = ",")
    }
}

data class IntegerStepField(
    @GsonNullable
    @SerializedName("value") val fieldValue: Int?,
) : StepField<Int>() {
    override fun validate(inputValue: Int?): Rlt<Any> {
        if (validate?.required == null) {
            return Rlt.Success(Unit)
        }
        if (validate.required) {
            if (inputValue == null) {
                return Rlt.Failed(RequireValueNullError())
            }
        }
        if (inputValue != null && validate.numLimit != null && validate.numLimit.isValid()) {
            if (inputValue < validate.numLimit.min) {
                return Rlt.Failed(NumberLessThanMinError())
            }
            if (inputValue > validate.numLimit.max) {
                return Rlt.Failed(NumberMoreThanMaxError())
            }
        }

        return Rlt.Success(Unit)
    }

    override fun getFieldValueDesc(): String? {
        return fieldValue?.let {
            formatNumberStr(it, false)
        }
    }
}

data class IntegerArrayStepField(
    @GsonNullable
    @SerializedName("value") val fieldValue: List<Int>?,
) : StepField<List<Int>>() {
    override fun validate(inputValue: List<Int>?): Rlt<Any> {
        if (validate?.required == null) {
            return Rlt.Success(Unit)
        }
        if (validate.required) {
            if (inputValue == null) {
                return Rlt.Failed(RequireValueNullError())
            }
        }
        if (validate.arraySize != null && validate.arraySize.isValid()) {
            val inputLength = inputValue?.size ?: 0
            if (inputLength < validate.arraySize.min) {
                return Rlt.Failed(ArrayLessThanMinError())
            }
            if (inputLength > validate.arraySize.max) {
                return Rlt.Failed(ArrayMoreThanMaxError())
            }
        }

        if (inputValue != null && validate.numLimit != null && validate.numLimit.isValid()) {
            for (input in inputValue) {
                if (input < validate.numLimit.min) {
                    return Rlt.Failed(NumberLessThanMinError())
                }
                if (input > validate.numLimit.max) {
                    return Rlt.Failed(NumberMoreThanMaxError())
                }
            }
        }

        return Rlt.Success(Unit)
    }

    override fun getFieldValueDesc(): String? {
        return fieldValue?.joinToString(separator = ",") {
            formatNumberStr(it, false)
        }
    }
}

data class DoubleStepField(
    @GsonNullable
    @SerializedName("value") val fieldValue: Double?,
) : StepField<Double>() {
    override fun validate(inputValue: Double?): Rlt<Any> {
        if (validate?.required == null) {
            return Rlt.Success(Unit)
        }
        if (validate.required) {
            if (inputValue == null) {
                return Rlt.Failed(RequireValueNullError())
            }
        }
        if (inputValue != null && validate.numLimit != null && validate.numLimit.isValid()) {
            if (inputValue < validate.numLimit.min) {
                return Rlt.Failed(NumberLessThanMinError())
            }
            if (inputValue > validate.numLimit.max) {
                return Rlt.Failed(NumberMoreThanMaxError())
            }
        }

        return Rlt.Success(Unit)
    }

    override fun getFieldValueDesc(): String? {
        return fieldValue?.let {
            formatNumberStr(it, false)
        }
    }
}

data class DoubleArrayStepField(
    @GsonNullable
    @SerializedName("value") val fieldValue: List<Double>?,
) : StepField<List<Double>>() {
    override fun validate(inputValue: List<Double>?): Rlt<Any> {
        if (validate?.required == null) {
            return Rlt.Success(Unit)
        }
        if (validate.required) {
            if (inputValue == null) {
                return Rlt.Failed(RequireValueNullError())
            }
        }
        if (validate.arraySize != null && validate.arraySize.isValid()) {
            val inputLength = inputValue?.size ?: 0
            if (inputLength < validate.arraySize.min) {
                return Rlt.Failed(ArrayLessThanMinError())
            }
            if (inputLength > validate.arraySize.max) {
                return Rlt.Failed(ArrayMoreThanMaxError())
            }
        }
        if (inputValue != null && validate.numLimit != null && validate.numLimit.isValid()) {
            for (input in inputValue) {
                if (input < validate.numLimit.min) {
                    return Rlt.Failed(NumberLessThanMinError())
                }
                if (input > validate.numLimit.max) {
                    return Rlt.Failed(NumberMoreThanMaxError())
                }
            }
        }

        return Rlt.Success(Unit)
    }

    override fun getFieldValueDesc(): String? {
        return fieldValue?.joinToString(separator = ",") {
            formatNumberStr(it, false)
        }
    }
}


/**
 * 数值
 */
sealed class StepFieldValue(
    @SerializedName("duration")
    var duration: Int? = null
)

data class StringStepFieldValue(
    @SerializedName("fieldCode") val fieldCode: String,
    @SerializedName("value") val value: String?
) : StepFieldValue()

data class StringArrayStepFieldValue(
    @SerializedName("fieldCode") val fieldCode: String,
    @SerializedName("value") val value: List<String>?
) : StepFieldValue()

data class IntegerStepFieldValue(
    @SerializedName("fieldCode") val fieldCode: String,
    @SerializedName("value") val value: Int?
) : StepFieldValue()

data class IntegerArrayStepFieldValue(
    @SerializedName("fieldCode") val fieldCode: String,
    @SerializedName("value") val value: List<Int>?
) : StepFieldValue()

data class DoubleStepFieldValue(
    @SerializedName("fieldCode") val fieldCode: String,
    @SerializedName("value") val value: Double?
) : StepFieldValue()

data class DoubleArrayStepFieldValue(
    @SerializedName("fieldCode") val fieldCode: String,
    @SerializedName("value") val value: List<Double>?
) : StepFieldValue()


@Parcelize
data class StepFieldConstant(
    @SerializedName("key") val key: String,
    @SerializedName("value") val value: String,
) : Parcelable

@Parcelize
data class StepFieldValidate(
    @GsonNullable
    @SerializedName("required") val required: Boolean?, //是否必填
    @GsonNullable
    @SerializedName("size") val size: StepFieldValueSize?, //输入内容长度限制
    @GsonNullable
    @SerializedName("numLimit") val numLimit: StepFieldNumLimit?, //数字最大,最小
    @GsonNullable
    @SerializedName("arraySize") val arraySize: StepFieldArraySize?, //数组长度(多选)
    @GsonNullable
    @SerializedName("regex") val desc: String?, //正则
) : Parcelable

@Parcelize
data class StepFieldValueSize(
    @SerializedName("min") val min: Int, //最小长度
    @SerializedName("max") val max: Int, //最大长度
) : Parcelable {
    fun isValid(): Boolean {
        return true
        //return min < max && max > 0
    }
}

@Parcelize
data class StepFieldNumLimit(
    @SerializedName("min") val min: Double, //最小数字
    @SerializedName("max") val max: Double, //最大数字
) : Parcelable {
    fun isValid(): Boolean {
        return true
        //return min < max && max > 0
    }
}

@Parcelize
data class StepFieldArraySize(
    @SerializedName("min") val min: Int, //最小数字
    @SerializedName("max") val max: Int, //最大数字
) : Parcelable {
    fun isValid(): Boolean {
        return true
        //return min < max && max > 0
    }
}


enum class ExampleType(val type: Int) {
    IMAGE(0),  //图片
    VOICE(1);  //图片
//    VIDEO(0),  //图片
//    URL(0),  //图片

    companion object {
        @JvmStatic
        fun map(type: Int?): ExampleType? {
            type ?: return null
            return entries.find { it.type == type }
        }
    }
}

@Parcelize
data class StepFieldExample(
    @SerializedName("type") val type: Int, //0:图片，1:声音，2:视频，3:url
    @GsonNullable
    @SerializedName("title") val title: String?, //标题
    @GsonNullable
    @SerializedName("desc") val desc: String?, //描述
    @GsonNullable
    @SerializedName("value") val value: String?, //数值
) : Parcelable {
    fun isValid(): Boolean {
        return !title.isNullOrEmpty() || !desc.isNullOrEmpty() || !value.isNullOrEmpty()
    }
}
