package dp.kotul.templategroupsbackend

import com.ninjasquad.springmockk.MockkBean
import dp.kotul.starter.service.AuthorizationService
import dp.kotul.templategroupsbackend.client.SolrApiClient
import dp.kotul.templategroupsbackend.data.TemplatesSolrData.templateSolrResponse
import dp.kotul.templategroupsbackend.data.TemplatesSolrData.templateSolrResponseAfterRemovedTemplate
import dp.kotul.templategroupsbackend.data.TemplatesSolrData.templateSolrResponseWithAddedVariables
import dp.kotul.templategroupsbackend.data.TemplatesSolrData.templateSolrResponseWithRemovedTemplateAndAddedRemovedVariables
import dp.kotul.templategroupsbackend.data.TemplatesSolrData.templateSolrResponseWithRemovedVariables
import dp.kotul.templategroupsbackend.data.templategroup.TemplateGroupCreateData.templateGroupCreateRequest
import dp.kotul.templategroupsbackend.data.templategroup.TemplateGroupGetData.templateGroupGetResponse
import dp.kotul.templategroupsbackend.data.templategroup.TemplateGroupGetData.templateGroupGetResponseAfterAddedVariables
import dp.kotul.templategroupsbackend.data.templategroup.TemplateGroupGetData.templateGroupGetResponseAfterRemovedTemplateAddedRemovedVariables
import dp.kotul.templategroupsbackend.data.templategroup.TemplateGroupGetData.templateGroupGetResponseAfterRemovedVariables
import dp.kotul.templategroupsbackend.data.templategroup.TemplateGroupGetData.templateGroupGetResponseWithoutTemplates
import dp.kotul.templategroupsbackend.data.templategroup.TemplateGroupUpdateData.templateGroupNotIncludedTemplateVariablesUpdateRequest
import dp.kotul.templategroupsbackend.data.templategroup.TemplateGroupUpdateData.templateGroupTransitiveDependencyTemplateVariablesUpdateRequest
import dp.kotul.templategroupsbackend.data.templategroup.TemplateGroupUpdateData.templateGroupUpdateRequest
import dp.kotul.templategroupsbackend.data.templategroup.laeId
import dp.kotul.templategroupsbackend.data.templategroup.nonExistingTemplateGroupId
import dp.kotul.templategroupsbackend.data.templategroupvariable.TemplateGroupVariableData.Companion.templateGroupVariablesRequestResponse
import dp.kotul.templategroupsbackend.exception.NotIncludedTemplateVariableException
import dp.kotul.templategroupsbackend.exception.TransitiveTemplateVariableDependencyException
import dp.kotul.templategroupsbackend.exception.WrongTemplateGroupSetupException
import dp.kotul.templategroupsbackend.facade.TemplateGroupFacadeImpl
import dp.kotul.templategroupsbackend.facade.TemplateGroupVariableFacadeImpl
import dp.kotul.templategroupsbackend.helper.SolrSearchHelper
import dp.kotul.templategroupsbackend.repository.TemplateGroupRepository
import dp.kotul.templategroupsbackend.repository.TemplateGroupVariableRepository
import dp.kotul.templategroupsbackend.repository.TemplateRepository
import dp.kotul.templategroupsbackend.repository.TemplateVariableRepository
import dp.kotul.templategroupsbackend.repository.VariableSourceRepository
import io.mockk.every
import org.assertj.core.api.Assertions
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Import
import org.springframework.test.annotation.DirtiesContext
import org.springframework.test.annotation.DirtiesContext.ClassMode
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.junit4.SpringRunner
import org.springframework.transaction.annotation.Transactional
import javax.persistence.EntityManager

@RunWith(SpringRunner::class)
@SpringBootTest(classes = [TemplateGroupsBackendApplication::class])
@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
@Import(PostgreSQLContainerDatabase::class)
@ActiveProfiles("repository-test", "test")
@Transactional
class TemplateGroupFacadeTest {

    @Autowired
    lateinit var templateGroupRepository: TemplateGroupRepository

    @Autowired
    lateinit var templateRepository: TemplateRepository

    @Autowired
    lateinit var templateVariableRepository: TemplateVariableRepository

    @Autowired
    lateinit var templateGroupVariableRepository: TemplateGroupVariableRepository

    @Autowired
    lateinit var variableSourceRepository: VariableSourceRepository

    @Autowired
    lateinit var entityManager: EntityManager

    @MockkBean(relaxed = true)
    lateinit var authService: AuthorizationService

    @MockkBean(relaxed = true)
    lateinit var solrSearchHelper: SolrSearchHelper

    @MockkBean(relaxed = true)
    lateinit var solrApiClient: SolrApiClient

    lateinit var templateGroupFacade: TemplateGroupFacadeImpl

    lateinit var templateGroupVariableFacade: TemplateGroupVariableFacadeImpl

    @Before
    fun setup() {
        templateGroupFacade = TemplateGroupFacadeImpl(authService, templateGroupRepository, templateRepository,
            templateVariableRepository, templateGroupVariableRepository, variableSourceRepository, solrSearchHelper, solrApiClient)

        templateGroupVariableFacade = TemplateGroupVariableFacadeImpl(authService, entityManager, templateGroupRepository,
            templateGroupVariableRepository, solrSearchHelper)
    }

    @Test
    fun `should create and get template group with correct solr templates`() {
        val templateGroup = templateGroupFacade.createTemplateGroup(templateGroupCreateRequest)
        prepareGetSolrTemplates()
        val currentTemplateGroupResponse = templateGroupFacade.getTemplateGroup(templateGroup.templateGroupId, laeId)

        Assertions.assertThat(currentTemplateGroupResponse).isEqualToComparingFieldByField(templateGroupGetResponse)
    }

    private fun prepareGetSolrTemplates() {
        every { solrSearchHelper.getSolrTemplatesResponse(any(), any(), any(), any()) } returns templateSolrResponse
    }

    @Test
    fun `should get null template group`() {
        val templateGroupDb = templateGroupFacade.getTemplateGroup(nonExistingTemplateGroupId, laeId)
        Assertions.assertThat(templateGroupDb).isNull()
    }

    @Test
    fun `should update template group with template group variables`() {
        val templateGroup = templateGroupFacade.createTemplateGroup(templateGroupCreateRequest)
        templateGroupFacade.updateTemplateGroup(templateGroupUpdateRequest)
        prepareGetSolrTemplates()
        val currentTemplateGroupResponse = templateGroupFacade.getTemplateGroup(templateGroup.templateGroupId, laeId)

        Assertions.assertThat(currentTemplateGroupResponse).isEqualToComparingFieldByField(templateGroupGetResponse)

        prepareGetSolrTemplatesByTemplateIds()
        val currentTemplateGroupVariablesResponse = templateGroupVariableFacade.getTemplateGroupVariables(templateGroup.templateGroupId, laeId)

        Assertions.assertThat(currentTemplateGroupVariablesResponse).isEqualTo(templateGroupVariablesRequestResponse)
    }

    @Test(expected = TransitiveTemplateVariableDependencyException::class)
    fun `should throw TransitiveTemplateVariableDependencyException during update template group`() {
        templateGroupFacade.createTemplateGroup(templateGroupCreateRequest)
        templateGroupFacade.updateTemplateGroup(templateGroupTransitiveDependencyTemplateVariablesUpdateRequest)
    }

    @Test(expected = NotIncludedTemplateVariableException::class)
    fun `should throw NotIncludedTemplateVariableException during update template group`() {
        templateGroupFacade.createTemplateGroup(templateGroupCreateRequest)
        templateGroupFacade.updateTemplateGroup(templateGroupNotIncludedTemplateVariablesUpdateRequest)
    }

    @Test(expected = WrongTemplateGroupSetupException::class)
    fun `should throw WrongTemplateGroupSetupException during get template group detail`() {
        val templateGroup = templateGroupFacade.createTemplateGroup(templateGroupCreateRequest)
        templateGroupFacade.updateTemplateGroup(templateGroupUpdateRequest)
        prepareGetSolrTemplatesWithRemovedVariables()
        templateGroupFacade.getTemplateGroupDetail(templateGroup.templateGroupId, laeId)
    }

    @Test
    fun `should get template group with removed templates, that were removed from solr`() {
        val templateGroup = templateGroupFacade.createTemplateGroup(templateGroupCreateRequest)
        val currentTemplateGroupResponse = templateGroupFacade.getTemplateGroup(templateGroup.templateGroupId, laeId)

        Assertions.assertThat(currentTemplateGroupResponse).isEqualToComparingFieldByField(templateGroupGetResponseWithoutTemplates)
    }

    @Test
    fun `should get template group with removed template variables, that were removed from solr`() {
        val templateGroup = templateGroupFacade.createTemplateGroup(templateGroupCreateRequest)
        prepareGetSolrTemplatesWithRemovedVariables()
        val currentTemplateGroupResponse = templateGroupFacade.getTemplateGroup(templateGroup.templateGroupId, laeId)

        Assertions.assertThat(currentTemplateGroupResponse).isEqualToComparingFieldByField(templateGroupGetResponseAfterRemovedVariables)
    }

    @Test
    fun `should get template group with added template variables, that were added to solr`() {
        val templateGroup = templateGroupFacade.createTemplateGroup(templateGroupCreateRequest)
        prepareGetSolrTemplatesWithAddedVariables()
        val currentTemplateGroupResponse = templateGroupFacade.getTemplateGroup(templateGroup.templateGroupId, laeId)

        Assertions.assertThat(currentTemplateGroupResponse).isEqualToComparingFieldByField(templateGroupGetResponseAfterAddedVariables)
    }

    @Test
    fun `should get template group with removed template and added-removed template variables`() {
        val templateGroup = templateGroupFacade.createTemplateGroup(templateGroupCreateRequest)
        prepareGetSolrTemplatesWithRemovedTemplateAddedRemovedVariables()
        val currentTemplateGroupResponse = templateGroupFacade.getTemplateGroup(templateGroup.templateGroupId, laeId)

        Assertions.assertThat(currentTemplateGroupResponse).isEqualToComparingFieldByField(templateGroupGetResponseAfterRemovedTemplateAddedRemovedVariables)
    }

    private fun prepareGetSolrTemplatesAfterTemplateRemove() {
        every { solrSearchHelper.getSolrTemplatesResponse(any(), any(), any(), any()) } returns templateSolrResponseAfterRemovedTemplate
    }

    private fun prepareGetSolrTemplatesByTemplateIds() {
        every { solrSearchHelper.getSolrTemplatesResponse(laeId = any(), templates = any()) } returns templateSolrResponse
    }

    private fun prepareGetSolrTemplatesWithRemovedVariables() {
        every { solrSearchHelper.getSolrTemplatesResponse(any(), any(), any(), any()) } returns templateSolrResponseWithRemovedVariables
    }

    private fun prepareGetSolrTemplatesWithAddedVariables() {
        every { solrSearchHelper.getSolrTemplatesResponse(any(), any(), any(), any()) } returns templateSolrResponseWithAddedVariables
    }

    private fun prepareGetSolrTemplatesWithRemovedTemplateAddedRemovedVariables() {
        every { solrSearchHelper.getSolrTemplatesResponse(any(), any(), any(), any()) } returns templateSolrResponseWithRemovedTemplateAndAddedRemovedVariables
    }
}
