package dev.fyloz.trial.colorrecipesexplorer.model

import com.fasterxml.jackson.annotation.JsonIgnore
import dev.fyloz.trial.colorrecipesexplorer.model.validation.NullOrNotBlank
import java.util.*
import javax.persistence.*
import javax.validation.constraints.NotBlank
import javax.validation.constraints.NotNull

const val IDENTIFIER_MIX_TYPE_NAME = "mixType"

private const val MIX_ID_NULL_MESSAGE = "Un identifiant est requis"
private const val MIX_NAME_NULL_MESSAGE = "Un nom est requis"
private const val MIX_RECIPE_NULL_MESSAGE = "Un recette est requise"
private const val MIX_MATERIAL_TYPE_NULL_MESSAGE = "Un type de prodsuit est requis"

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

    var location: String?,

    @JsonIgnore
    @ManyToOne
    val recipe: Recipe,

    @ManyToOne(cascade = [CascadeType.PERSIST])
    var mixType: MixType,

    @OneToMany(cascade = [CascadeType.ALL])
    var mixMaterials: MutableCollection<MixMaterial>,
) : Model {
    constructor(recipe: Recipe, mixType: MixType) : this(null, null, recipe, mixType, mutableListOf())

    override fun equals(other: Any?): Boolean = other is Mix && recipe == other.recipe && mixType == other.mixType
    override fun hashCode(): Int = Objects.hash(recipe, mixType)
}

open class MixSaveDto(
    @field:NotBlank(message = MIX_NAME_NULL_MESSAGE)
    val name: String,

    @field:NotNull(message = MIX_RECIPE_NULL_MESSAGE)
    val recipeId: Long,

    @field:NotNull(message = MIX_MATERIAL_TYPE_NULL_MESSAGE)
    val materialTypeId: Long,

    val mixMaterials: Map<Long, Float>?
) : EntityDto<Mix> {
    override fun toEntity(): Mix = throw UnsupportedOperationException()
}

open class MixUpdateDto(
    @field:NotNull(message = MIX_ID_NULL_MESSAGE)
    val id: Long,

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

    val materialTypeId: Long?,

    val mixMaterials: Map<Long, Float>?
) : EntityDto<Mix> {
    override fun toEntity(): Mix = throw UnsupportedOperationException()
}

// ==== DSL ====
fun mix(
    id: Long? = null,
    location: String? = "location",
    recipe: Recipe = recipe(),
    mixType: MixType = mixType(),
    mixMaterials: MutableCollection<MixMaterial> = mutableListOf(),
    op: Mix.() -> Unit = {}
) = Mix(id, location, recipe, mixType, mixMaterials).apply(op)

fun mixSaveDto(
    name: String = "name",
    recipeId: Long = 0L,
    materialTypeId: Long = 0L,
    mixMaterials: Map<Long, Float>? = mapOf(),
    op: MixSaveDto.() -> Unit = {}
) = MixSaveDto(name, recipeId, materialTypeId, mixMaterials).apply(op)

fun mixUpdateDto(
    id: Long = 0L,
    name: String? = "name",
    materialTypeId: Long? = 0L,
    mixMaterials: Map<Long, Float>? = mapOf(),
    op: MixUpdateDto.() -> Unit = {}
) = MixUpdateDto(id, name, materialTypeId, mixMaterials).apply(op)
