package dev.fyloz.trial.colorrecipesexplorer.service

import dev.fyloz.trial.colorrecipesexplorer.model.*
import dev.fyloz.trial.colorrecipesexplorer.repository.MixRepository
import org.springframework.context.annotation.Lazy
import org.springframework.stereotype.Service
import javax.transaction.Transactional

interface MixService : ExternalModelService<Mix, MixSaveDto, MixUpdateDto, MixRepository> {
    /** Gets all mixes with the given [mixType]. */
    fun getAllByMixType(mixType: MixType): Collection<Mix>

    /** Checks if a [MixType] is shared by several [Mix]es or not. */
    fun mixTypeIsShared(mixType: MixType): Boolean

    /** Updates the location of the given [mix] to the given [location]. */
    fun updateLocation(mix: Mix, location: String)
}

@Service
class MixServiceImpl(
    mixRepository: MixRepository,
    @Lazy val recipeService: RecipeService,
    @Lazy val materialTypeService: MaterialTypeService,
    val mixMaterialService: MixMaterialService,
    val mixTypeService: MixTypeService
) : AbstractModelService<Mix, MixRepository>(mixRepository),
    MixService {
    override fun getAllByMixType(mixType: MixType): Collection<Mix> = repository.findAllByMixType(mixType)
    override fun mixTypeIsShared(mixType: MixType): Boolean = getAllByMixType(mixType).count() > 1

    @Transactional
    override fun save(entity: MixSaveDto): Mix {
        val recipe = recipeService.getById(entity.recipeId)
        val materialType = materialTypeService.getById(entity.materialTypeId)
        val mixType = MixType(entity.name, materialType)

        var mix = save(mix(recipe = recipe, mixType = mixType))
        val mixMaterials =
            if (entity.mixMaterials != null) mixMaterialService.createFromMap(mix, entity.mixMaterials) else listOf()
        mix = update(
            mix(
                id = mix.id,
                location = null,
                recipe = recipe,
                mixType = mixType,
                mixMaterials = mixMaterials.toMutableList()
            )
        )

        recipeService.addMix(recipe, mix)

        return mix
    }

    override fun update(entity: MixUpdateDto): Mix {
        val mix = getById(entity.id)
        if (entity.name != null || entity.materialTypeId != null) {
            mix.mixType = if (mixTypeIsShared(mix.mixType)) {
                mixTypeService.createForNameAndMaterialType(
                    entity.name ?: mix.mixType.name,
                    if (entity.materialTypeId != null) materialTypeService.getById(entity.materialTypeId) else mix.mixType.material.materialType!!
                )
            } else {
                mixTypeService.updateForNameAndMaterialType(
                    mix.mixType,
                    entity.name ?: mix.mixType.name,
                    if (entity.materialTypeId != null) materialTypeService.getById(entity.materialTypeId) else mix.mixType.material.materialType!!
                )
            }
        }
        if (entity.mixMaterials != null) {
            mix.mixMaterials = mixMaterialService.createFromMap(mix, entity.mixMaterials).toMutableList()
        }
        return update(mix)
    }

    override fun updateLocation(mix: Mix, location: String) {
        update(mix.apply { this.location = location })
    }

    @Transactional
    override fun delete(entity: Mix) {
        recipeService.removeMix(entity)
        super.delete(entity)
    }
}
