package org.nstart.dep265.requestservice.service

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoInteractions
import org.mockito.Mockito.`when`
import org.nstart.dep265.requestservice.dto.UpdateGridSettingsRequestDto
import org.nstart.dep265.requestservice.entity.EarthGridSettingsEntity
import org.nstart.dep265.requestservice.repository.EarthCellRepository
import org.nstart.dep265.requestservice.repository.EarthGridSettingsRepository
import org.nstart.dep265.requestservice.repository.RequestCellRepository
import java.util.Optional
import kotlin.test.assertEquals

class GridSettingsServiceTest {
    private val earthGridSettingsRepository = mock(EarthGridSettingsRepository::class.java).also { repository ->
        doAnswer { invocation -> invocation.getArgument<EarthGridSettingsEntity>(0) }
            .`when`(repository)
            .save(any(EarthGridSettingsEntity::class.java))
    }

    @Test
    fun `get settings returns current settings`() {
        `when`(earthGridSettingsRepository.findById(EarthGridSettingsEntity.DEFAULT_SETTINGS_ID))
            .thenReturn(Optional.of(EarthGridSettingsEntity(calculationStep = 4.0)))
        val service = GridSettingsService(earthGridSettingsRepository)

        val response = service.getSettings()

        assertEquals(EarthGridSettingsEntity.DEFAULT_SETTINGS_ID, response.settingsId)
        assertEquals(4.0, response.calculationStep)
        verify(earthGridSettingsRepository, never()).save(any(EarthGridSettingsEntity::class.java))
    }

    @Test
    fun `get settings creates and returns default settings when missing`() {
        `when`(earthGridSettingsRepository.findById(EarthGridSettingsEntity.DEFAULT_SETTINGS_ID))
            .thenReturn(Optional.empty())
        val service = GridSettingsService(earthGridSettingsRepository)

        val response = service.getSettings()

        val settingsCaptor = ArgumentCaptor.forClass(EarthGridSettingsEntity::class.java)
        verify(earthGridSettingsRepository).save(settingsCaptor.capture())
        assertEquals(EarthGridSettingsEntity.DEFAULT_SETTINGS_ID, settingsCaptor.value.settingsId)
        assertEquals(EarthGridSettingsEntity.DEFAULT_CALCULATION_STEP, settingsCaptor.value.calculationStep)
        assertEquals(EarthGridSettingsEntity.DEFAULT_SETTINGS_ID, response.settingsId)
        assertEquals(EarthGridSettingsEntity.DEFAULT_CALCULATION_STEP, response.calculationStep)
    }

    @Test
    fun `put settings updates calculation step`() {
        val currentSettings = EarthGridSettingsEntity(calculationStep = 2.0)
        `when`(earthGridSettingsRepository.findById(EarthGridSettingsEntity.DEFAULT_SETTINGS_ID))
            .thenReturn(Optional.of(currentSettings))
        val service = GridSettingsService(earthGridSettingsRepository)

        val response = service.updateSettings(UpdateGridSettingsRequestDto(calculationStep = 3.5))

        val settingsCaptor = ArgumentCaptor.forClass(EarthGridSettingsEntity::class.java)
        verify(earthGridSettingsRepository).save(settingsCaptor.capture())
        assertEquals(EarthGridSettingsEntity.DEFAULT_SETTINGS_ID, settingsCaptor.value.settingsId)
        assertEquals(3.5, settingsCaptor.value.calculationStep)
        assertEquals(3.5, response.calculationStep)
    }

    @Test
    fun `put settings does not rebuild earth cells delete request cells or project requests`() {
        val earthCellRepository = mock(EarthCellRepository::class.java)
        val requestCellRepository = mock(RequestCellRepository::class.java)
        val requestGridProjectionService = mock(RequestGridProjectionService::class.java)
        `when`(earthGridSettingsRepository.findById(EarthGridSettingsEntity.DEFAULT_SETTINGS_ID))
            .thenReturn(Optional.of(EarthGridSettingsEntity(calculationStep = 2.0)))
        val service = GridSettingsService(earthGridSettingsRepository)

        service.updateSettings(UpdateGridSettingsRequestDto(calculationStep = 1.0))

        verifyNoInteractions(earthCellRepository)
        verifyNoInteractions(requestCellRepository)
        verifyNoInteractions(requestGridProjectionService)
    }

    @Test
    fun `put settings rejects non-positive calculation step`() {
        val service = GridSettingsService(earthGridSettingsRepository)

        assertThrows<InvalidGridSettingsException> {
            service.updateSettings(UpdateGridSettingsRequestDto(calculationStep = 0.0))
        }
        verify(earthGridSettingsRepository, never()).save(any(EarthGridSettingsEntity::class.java))
    }
}
