package de.krisenvorrat.app.ui.camera

import android.content.ContentResolver
import android.content.Context
import android.net.Uri
import de.krisenvorrat.app.data.remote.OpenAiVisionService
import de.krisenvorrat.app.domain.model.ItemFormPrefill
import de.krisenvorrat.app.domain.model.SettingsKey
import de.krisenvorrat.app.domain.repository.SettingsRepository
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import java.io.ByteArrayInputStream

@OptIn(ExperimentalCoroutinesApi::class)
class CameraViewModelTest {

    private val testDispatcher = StandardTestDispatcher()
    private lateinit var mockOpenAiVisionService: OpenAiVisionService
    private lateinit var mockSettingsRepository: SettingsRepository
    private lateinit var mockContext: Context
    private lateinit var mockContentResolver: ContentResolver
    private lateinit var viewModel: CameraViewModel

    @Before
    fun setup() {
        Dispatchers.setMain(testDispatcher)
        mockOpenAiVisionService = mockk()
        mockSettingsRepository = mockk()
        mockContext = mockk(relaxed = true)
        mockContentResolver = mockk(relaxed = true)
        every { mockContext.contentResolver } returns mockContentResolver
    }

    @After
    fun tearDown() {
        Dispatchers.resetMain()
    }

    private fun createViewModel(): CameraViewModel =
        CameraViewModel(mockOpenAiVisionService, mockSettingsRepository, testDispatcher, mockContext)

    @Test
    fun test_initialState_noPhoto_stateIsEmpty() = runTest(testDispatcher) {
        // Given
        coEvery { mockSettingsRepository.getString(any()) } returns ""
        viewModel = createViewModel()

        // When
        advanceUntilIdle()

        // Then
        val state = viewModel.uiState.value
        assertNull(state.capturedImageUri)
        assertFalse(state.isAnalyzing)
        assertTrue(state.recognizedItems.isEmpty())
        assertTrue(state.selectedItems.isEmpty())
        assertNull(state.errorMessage)
        assertFalse(state.hasApiKey)
    }

    @Test
    fun test_analyzePhoto_noApiKey_setsErrorMessage() = runTest(testDispatcher) {
        // Given
        coEvery { mockSettingsRepository.getString(any()) } returns ""
        viewModel = createViewModel()
        advanceUntilIdle()
        val mockUri = mockk<Uri>()
        viewModel.onPhotoCaptured(mockUri)

        // When
        viewModel.analyzePhoto()
        advanceUntilIdle()

        // Then
        assertEquals(
            "Bitte OpenAI API-Key in den Einstellungen hinterlegen",
            viewModel.uiState.value.errorMessage
        )
    }

    @Test
    fun test_toggleItemSelection_selectsItem() = runTest(testDispatcher) {
        // Given
        coEvery { mockSettingsRepository.getString(any()) } returns ""
        viewModel = createViewModel()
        advanceUntilIdle()
        assertFalse(viewModel.uiState.value.selectedItems.contains(0))

        // When
        viewModel.toggleItemSelection(0)

        // Then
        assertTrue(viewModel.uiState.value.selectedItems.contains(0))
    }

    @Test
    fun test_toggleItemSelection_deselectsItem() = runTest(testDispatcher) {
        // Given
        coEvery { mockSettingsRepository.getString(any()) } returns ""
        viewModel = createViewModel()
        advanceUntilIdle()
        viewModel.toggleItemSelection(1)
        assertTrue(viewModel.uiState.value.selectedItems.contains(1))

        // When
        viewModel.toggleItemSelection(1)

        // Then
        assertFalse(viewModel.uiState.value.selectedItems.contains(1))
    }

    @Test
    fun test_selectAll_selectsAllItems() = runTest(testDispatcher) {
        // Given
        val items = listOf(
            ItemFormPrefill(name = "Brot"),
            ItemFormPrefill(name = "Wasser"),
            ItemFormPrefill(name = "Konserven")
        )
        coEvery { mockSettingsRepository.getString(any()) } returns "test-api-key"
        coEvery { mockOpenAiVisionService.analyzeImage(any(), any()) } returns items

        val mockUri = mockk<Uri>()
        every { mockContentResolver.openInputStream(mockUri) } answers { ByteArrayInputStream(ByteArray(100)) }

        viewModel = createViewModel()
        advanceUntilIdle()
        viewModel.onPhotoCaptured(mockUri)
        viewModel.analyzePhoto()
        advanceUntilIdle()
        assertEquals(3, viewModel.uiState.value.recognizedItems.size)

        // When
        viewModel.selectAll()

        // Then
        assertEquals(setOf(0, 1, 2), viewModel.uiState.value.selectedItems)
    }

    @Test
    fun test_analyzePhoto_apiReturnsItems_updatesRecognizedItems() = runTest(testDispatcher) {
        // Given
        val items = listOf(ItemFormPrefill(name = "Brot"), ItemFormPrefill(name = "Wasser"))
        coEvery { mockSettingsRepository.getString(any()) } returns "test-api-key"
        coEvery { mockOpenAiVisionService.analyzeImage(any(), any()) } returns items
        every { mockContentResolver.openInputStream(any()) } answers { ByteArrayInputStream(ByteArray(100)) }
        val mockUri = mockk<Uri>()

        viewModel = createViewModel()
        advanceUntilIdle()
        viewModel.onPhotoCaptured(mockUri)
        advanceUntilIdle()

        // When
        viewModel.analyzePhoto()
        advanceUntilIdle()

        // Then
        assertEquals(2, viewModel.uiState.value.recognizedItems.size)
        assertFalse(viewModel.uiState.value.isAnalyzing)
    }

    @Test
    fun test_analyzePhoto_photoNotReadable_setsError() = runTest(testDispatcher) {
        // Given – onPhotoCaptured was NOT called, capturedImageUri = null
        coEvery { mockSettingsRepository.getString(any()) } returns "test-api-key"
        viewModel = createViewModel()
        advanceUntilIdle()

        // When
        viewModel.analyzePhoto()
        advanceUntilIdle()

        // Then
        assertNotNull(viewModel.uiState.value.errorMessage)
    }

    @Test
    fun test_onAddFirstSelectedItem_noSelection_doesNotSetNavigation() = runTest(testDispatcher) {
        // Given – selectedItems is empty
        coEvery { mockSettingsRepository.getString(any()) } returns ""
        viewModel = createViewModel()
        advanceUntilIdle()

        // When
        viewModel.onAddFirstSelectedItem()

        // Then
        assertNull(viewModel.uiState.value.navigateToPrefillJson)
    }
}
