package dev.fyloz.trial.colorrecipesexplorer.service

import com.nhaarman.mockitokotlin2.*
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityAlreadyExistsRestException
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityNotFoundRestException
import dev.fyloz.trial.colorrecipesexplorer.model.*
import dev.fyloz.trial.colorrecipesexplorer.repository.MixTypeRepository
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class MixTypeServiceTest : AbstractNamedModelServiceTest<MixType, MixTypeService, MixTypeRepository>() {
    override val repository: MixTypeRepository = mock()
    private val materialService: MaterialService = mock()
    private val mixService: MixService = mock()
    override val service: MixTypeService = spy(MixTypeServiceImpl(repository, materialService, mixService))

    private val material: Material = material(id = 0L)
    override val entity: MixType = mixType(id = 0L, name = "mix type", material = material)
    override val anotherEntity: MixType = mixType(id = 1L, name = "another mix type")
    override val entityWithEntityName: MixType = mixType(id = 2L, name = entity.name)

    @AfterEach
    override fun afterEach() {
        reset(materialService)
        super.afterEach()
    }

    @Nested
    inner class GetByMaterial {
        @Test
        fun `returns the mix type with the given material`() {
            whenever(repository.findByMaterial(material)).doReturn(entity)

            val found = service.getByMaterial(material)

            assertEquals(entity, found)
        }

        @Test
        fun `throws EntityNotFoundRestException when no mix type with the given material exists`() {
            whenever(repository.findByMaterial(material)).doReturn(null)

            val exception = assertThrows<EntityNotFoundRestException> { service.getByMaterial(material) }
            assertEquals(material.name, exception.value)
        }
    }

    @Nested
    inner class SaveMixType {
        @Test
        fun `throws EntityAlreadyExistsRestException when a material with the name of the new mix type exists`() {
            whenever(materialService.existsByName(entity.name)).doReturn(true)

            val exception = assertThrows<EntityAlreadyExistsRestException> { service.save(entity) }
            assertEquals(entity.name, exception.value)
        }
    }

    @Nested
    inner class CreateForNameAndMaterialType {
        @Test
        fun `creates a save a valid mix type with the given name and material type`() {
            val name = entity.name
            val materialType = materialType()

            doAnswer { it.arguments[0] }.whenever(service).save(any())

            val found = service.createForNameAndMaterialType(name, materialType)

            verify(service).save(any())

            assertEquals(name, found.name)
            assertEquals(name, found.material.name)
            assertEquals(materialType, found.material.materialType)
            assertTrue(found.material.isMixType)
        }
    }

    @Nested
    inner class UpdateForNameAndMaterialType {
        @Test
        fun `updates the given mix type with the given name and material type`() {
            val mixType = mixType(id = 1L, material = material(isMixType = true))
            val name = entity.name
            val materialType = materialType()

            doAnswer { it.arguments[0] }.whenever(service).update(any())

            val found = service.updateForNameAndMaterialType(mixType, name, materialType)

            verify(service).update(any())

            assertEquals(mixType.id, found.id)
            assertEquals(name, found.name)
            assertEquals(name, found.material.name)
            assertEquals(materialType, found.material.materialType)
            assertTrue(found.material.isMixType)
        }
    }
}
