package dev.fyloz.trial.colorrecipesexplorer.service

import com.nhaarman.mockitokotlin2.*
import dev.fyloz.trial.colorrecipesexplorer.model.*
import dev.fyloz.trial.colorrecipesexplorer.repository.MixRepository
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals

class MixServiceTest : AbstractExternalModelServiceTest<Mix, MixSaveDto, MixUpdateDto, MixService, MixRepository>() {
    override val repository: MixRepository = mock()
    private val recipeService: RecipeService = mock()
    private val materialTypeService: MaterialTypeService = mock()
    private val mixMaterialService: MixMaterialService = mock()
    private val mixTypeService: MixTypeService = mock()
    override val service: MixService =
        spy(MixServiceImpl(repository, recipeService, materialTypeService, mixMaterialService, mixTypeService))

    override val entity: Mix = mix(id = 0L, location = "location")
    override val anotherEntity: Mix = mix(id = 1L)
    override val entitySaveDto: MixSaveDto = spy(mixSaveDto(mixMaterials = mapOf(1L to 1000f)))
    override val entityUpdateDto: MixUpdateDto = spy(mixUpdateDto(id = entity.id!!))

    @Nested
    inner class GetAllByMixType {
        @Test
        fun `returns all mixes with the given mix type`() {
            val mixType = mixType(id = 0L)

            whenever(repository.findAllByMixType(mixType)).doReturn(entityList)

            val found = service.getAllByMixType(mixType)

            assertEquals(entityList, found)
        }
    }

    @Nested
    inner class SaveDto {
        @Test
        fun `calls and returns save() with the created entity`() {
            val recipe = recipe(id = entitySaveDto.recipeId)
            val materialType = materialType(id = entitySaveDto.materialTypeId)
            val material = material(
                name = entitySaveDto.name,
                inventoryQuantity = Float.MIN_VALUE,
                isMixType = true,
                materialType = materialType
            )
            val mixType = mixType(name = entitySaveDto.name, material = material)
            val mix = mix(recipe = recipe, mixType = mixType)
            val mixWithId = mix(id = 0L, recipe = recipe, mixType = mixType)
            val mixMaterials = listOf(mixMaterial(mix = mixWithId, material = material(id = 1L), quantity = 1000f))
            val mixWithMaterials = mix.apply { this.mixMaterials.addAll(mixMaterials) }

            whenever(recipeService.getById(recipe.id!!)).doReturn(recipe)
            whenever(materialTypeService.getById(materialType.id!!)).doReturn(materialType)
            whenever(mixMaterialService.createFromMap(mixWithId, entitySaveDto.mixMaterials!!)).doReturn(mixMaterials)
            doReturn(true).whenever(service).existsById(mixWithId.id!!)
            doReturn(mixWithId).whenever(service).save(mix)
            doReturn(mixWithMaterials).whenever(service).update(mixWithMaterials)

            val found = service.save(entitySaveDto)

            verify(service).save(mix)
            verify(service).update(mixWithMaterials)
            verify(recipeService).addMix(recipe, mix)

            assertEquals(mixWithMaterials, found)
        }
    }

    @Nested
    inner class UpdateLocation {
        @Test
        fun `calls update() with the given mix with the given location`() {
            val newLocation = "new location"
            val expected = entity.apply { location = newLocation }
            doReturn(expected).whenever(service).update(expected)

            service.updateLocation(entity, newLocation)

            verify(service).update(expected)
        }
    }
}
