package dev.fyloz.trial.colorrecipesexplorer.model

import dev.fyloz.trial.colorrecipesexplorer.model.validation.NullOrNotBlank
import dev.fyloz.trial.colorrecipesexplorer.model.validation.NullOrSize
import org.hibernate.annotations.ColumnDefault
import java.util.*
import javax.persistence.*
import javax.validation.constraints.NotBlank
import javax.validation.constraints.NotNull
import javax.validation.constraints.Size

private const val MATERIAL_TYPE_ID_NULL_MESSAGE = "Un identifiant est requis"
private const val MATERIAL_TYPE_NAME_NULL_MESSAGE = "Un nom est requis"
private const val MATERIAL_TYPE_PREFIX_NULL_MESSAGE = "Un préfixe est requis"
private const val MATERIAL_TYPE_PREFIX_SIZE_MESSAGE = "Le préfixe doit faire exactement 3 caractères"

const val IDENTIFIER_PREFIX_NAME = "prefix"

@Entity
data class MaterialType(
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        override val id: Long? = null,

        @Column(unique = true)
        override val name: String = "",

        @Column(unique = true)
        val prefix: String = "",

        @ColumnDefault("false")
        val usePercentages: Boolean = false,

        @ColumnDefault("false")
        val systemType: Boolean = false
) : NamedModel {
    override fun equals(other: Any?): Boolean = other is MaterialType && name == other.name && prefix == other.prefix
    override fun hashCode(): Int = Objects.hash(name, prefix)
}

open class MaterialTypeSaveDto(
        @field:NotBlank(message = MATERIAL_TYPE_NAME_NULL_MESSAGE)
        val name: String,

        @field:NotBlank(message = MATERIAL_TYPE_PREFIX_NULL_MESSAGE)
        @field:Size(min = 3, max = 3, message = MATERIAL_TYPE_PREFIX_SIZE_MESSAGE)
        val prefix: String,

        val usePercentages: Boolean = false
) : EntityDto<MaterialType> {
    override fun toEntity(): MaterialType =
            MaterialType(null, name, prefix, usePercentages)
}

open class MaterialTypeUpdateDto(
        @field:NotNull(message = MATERIAL_TYPE_ID_NULL_MESSAGE)
        val id: Long,

        @field:NullOrNotBlank(message = MATERIAL_TYPE_NAME_NULL_MESSAGE)
        val name: String?,

        @field:NullOrSize(min = 3, max = 3, message = MATERIAL_TYPE_PREFIX_NULL_MESSAGE)
        val prefix: String?
) : EntityDto<MaterialType> {
    override fun toEntity(): MaterialType =
            MaterialType(id, name ?: "", prefix ?: "")
}

// ==== DSL ====
fun materialType(
        id: Long? = null,
        name: String = "name",
        prefix: String = "PRE",
        usePercentages: Boolean = false,
        systemType: Boolean = false,
        op: MaterialType.() -> Unit = {}
) = MaterialType(id, name, prefix, usePercentages, systemType).apply(op)

fun materialType(
        materialType: MaterialType,
        newId: Long? = null,
        newName: String? = null,
        newSystemType: Boolean? = null
) = with(materialType) { MaterialType(newId ?: id, newName ?: name, prefix, usePercentages, newSystemType ?: systemType) }

fun materialTypeSaveDto(
        name: String = "name",
        prefix: String = "PRE",
        usePercentages: Boolean = false,
        op: MaterialTypeSaveDto.() -> Unit = {}
) = MaterialTypeSaveDto(name, prefix, usePercentages).apply(op)

fun materialTypeUpdateDto(
        id: Long = 0L,
        name: String? = null,
        prefix: String? = null,
        op: MaterialTypeUpdateDto.() -> Unit = {}
) = MaterialTypeUpdateDto(id, name, prefix).apply(op)
