package ru.yandex.direct.api.v5.entity.dictionaries.converter

import com.yandex.direct.api.v5.dictionaries.EnumFilterFieldProps
import com.yandex.direct.api.v5.dictionaries.FilterFieldItem
import com.yandex.direct.api.v5.dictionaries.FilterFieldOperator
import com.yandex.direct.api.v5.dictionaries.FilterFieldType
import com.yandex.direct.api.v5.dictionaries.FilterSchemasItem
import com.yandex.direct.api.v5.dictionaries.NumberFilterFieldProps
import com.yandex.direct.api.v5.dictionaries.StringFilterFieldProps
import com.yandex.direct.api.v5.general.ArrayOfString
import com.yandex.direct.api.v5.general.StringConditionOperatorEnum
import ru.yandex.direct.core.entity.performancefilter.model.Operator
import ru.yandex.direct.core.entity.performancefilter.model.Operator.CONTAINS
import ru.yandex.direct.core.entity.performancefilter.model.Operator.EQUALS
import ru.yandex.direct.core.entity.performancefilter.model.Operator.EXISTS
import ru.yandex.direct.core.entity.performancefilter.model.Operator.GREATER
import ru.yandex.direct.core.entity.performancefilter.model.Operator.LESS
import ru.yandex.direct.core.entity.performancefilter.model.Operator.NOT_CONTAINS
import ru.yandex.direct.core.entity.performancefilter.model.Operator.RANGE
import ru.yandex.direct.core.entity.performancefilter.schema.FilterSchema
import ru.yandex.direct.core.entity.performancefilter.schema.FilterSchema.AVAILABLE
import ru.yandex.direct.core.entity.performancefilter.schema.FilterSchema.EMPTY_SCHEMA_NAME
import ru.yandex.direct.core.entity.performancefilter.schema.FilterSchemaFieldValidator
import ru.yandex.direct.core.entity.performancefilter.schema.model.EnumFieldParams
import ru.yandex.direct.core.entity.performancefilter.schema.model.NumberFieldParams
import ru.yandex.direct.core.entity.performancefilter.schema.model.StringFieldParams
import ru.yandex.direct.core.entity.performancefilter.schema.parser.ObjectListParser
import java.math.BigDecimal
import kotlin.collections.Map.Entry

val CORE_OPERATOR_BY_API_OPERATOR = mapOf(
    StringConditionOperatorEnum.GREATER_THAN to GREATER,
    StringConditionOperatorEnum.LESS_THAN to LESS,
    StringConditionOperatorEnum.IN_RANGE to RANGE,
    StringConditionOperatorEnum.EXISTS to EXISTS,
    StringConditionOperatorEnum.EQUALS_ANY to EQUALS,
    StringConditionOperatorEnum.CONTAINS_ANY to CONTAINS,
    StringConditionOperatorEnum.NOT_CONTAINS_ALL to NOT_CONTAINS
)

val API_OPERATOR_BY_CORE_OPERATOR = CORE_OPERATOR_BY_API_OPERATOR.entries.associate { (k, v) -> v to k }

fun getEmptyFilterSchemasItem() =
    FilterSchemasItem().apply {
        name = EMPTY_SCHEMA_NAME
        fields = emptyList()
    }

fun toFilterSchemasItem(filterSchema: FilterSchema) =
    FilterSchemasItem().apply {
        name = filterSchema.filterSchemaName
        fields = filterSchema.filterFieldValidatorsMap
            .filter(::filterField)
            .map(::toFilterFieldItem)
            .sortedBy { it.name }
            .toList()
    }

/**
 * Реализует бизнес-логику, какие поля не показываем в схеме отдаваемой внешним пользователям.
 */
private fun filterField(fieldEntry: Entry<String, FilterSchemaFieldValidator>) =
    /* Поле "available" не показываем, т.к. оно обрабатывается особым образом и в API (да и в UI) реализовано как
     самостоятельный фильтр.*/
    fieldEntry.key != AVAILABLE

private fun toFilterFieldItem(entry: Entry<String, FilterSchemaFieldValidator>): FilterFieldItem {
    val item = FilterFieldItem().apply { name = entry.key }

    item.operators = entry.value.operators
        .map { toFilterFieldOperator(it, entry.value) }
        .sortedBy { it.type }
        .toList()

    when (val fieldParams = entry.value.fieldParams) {
        is EnumFieldParams -> {
            item.type = FilterFieldType.ENUM
            item.enumProps = EnumFilterFieldProps().apply {
                values = fieldParams.enumValues
                    .sortedWith(compareBy({ it.length }, { it }))
                    .toArrayOfString()
            }
        }
        is NumberFieldParams -> {
            item.type = FilterFieldType.NUMBER
            item.numberProps = NumberFilterFieldProps().apply {
                max = BigDecimal.valueOf(fieldParams.maxValue)
                min = BigDecimal.valueOf(fieldParams.minValue)
                precision = fieldParams.precision
            }
        }
        is StringFieldParams -> {
            item.type = FilterFieldType.STRING
            item.stringProps = StringFilterFieldProps().apply {
                maxLength = fieldParams.maxStringLength
                minLength = fieldParams.minStringLength
            }
        }
        else -> error("Unknown fieldParams type: ${fieldParams::javaClass.name}")
    }
    return item
}

private fun toFilterFieldOperator(operator: Operator, validator: FilterSchemaFieldValidator) =
    FilterFieldOperator().apply {
        maxItems = getMaxItems(validator, operator)
        type = API_OPERATOR_BY_CORE_OPERATOR[operator]
    }

private fun getMaxItems(validator: FilterSchemaFieldValidator, operator: Operator) =
    validator.getParser(operator)
        .let { it as? ObjectListParser<*> }
        ?.maxItemCount
        ?: 1

private fun Collection<String>.toArrayOfString(): ArrayOfString =
    this.toList().let { ArrayOfString().apply { items = it } }
