package de.krisenvorrat.app.ui.messaging

import androidx.lifecycle.SavedStateHandle
import de.krisenvorrat.app.data.db.entity.MessageEntity
import de.krisenvorrat.app.domain.repository.MessageRepository
import de.krisenvorrat.shared.model.UserListItemDto
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
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.assertTrue
import org.junit.Before
import org.junit.Test

private class FakeChatMessageRepository(
    private val myUserId: String = "user1",
    private val conversation: MutableStateFlow<List<MessageEntity>> = MutableStateFlow(emptyList())
) : MessageRepository {
    var sendMessageCalled = false
    var lastSentBody: String? = null

    override fun getConversation(myId: String, otherId: String): Flow<List<MessageEntity>> = conversation
    override suspend fun sendMessage(recipientId: String, body: String) {
        sendMessageCalled = true
        lastSentBody = body
    }
    override suspend fun fetchUsers(): Result<List<UserListItemDto>> = Result.success(emptyList())
    override suspend fun getMyUserId(): String = myUserId
    override suspend fun drainPendingMessages() = Unit
}

@OptIn(ExperimentalCoroutinesApi::class)
class ChatViewModelTest {

    private val testDispatcher = StandardTestDispatcher()

    @Before
    fun setup() {
        Dispatchers.setMain(testDispatcher)
    }

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

    private fun createViewModel(
        recipientId: String = "user2",
        recipientUsername: String = "Bob",
        repo: MessageRepository = FakeChatMessageRepository()
    ): ChatViewModel {
        val savedStateHandle = SavedStateHandle(mapOf("recipientId" to recipientId, "recipientUsername" to recipientUsername))
        return ChatViewModel(savedStateHandle = savedStateHandle, messageRepository = repo)
    }

    @Test
    fun test_uiState_showsCorrectRecipientUsername() = runTest(testDispatcher) {
        // Given / When
        val viewModel = createViewModel(recipientUsername = "Bob")
        advanceUntilIdle()

        // Then
        assertEquals("Bob", viewModel.uiState.value.recipientUsername)
    }

    @Test
    fun test_sendMessage_clearsInput() = runTest(testDispatcher) {
        // Given
        val viewModel = createViewModel()
        advanceUntilIdle()
        viewModel.onInputChanged("Hello")

        // When
        viewModel.sendMessage()
        advanceUntilIdle()

        // Then
        assertEquals("", viewModel.uiState.value.inputText)
    }

    @Test
    fun test_sendMessage_whenEmptyBody_doesNotSend() = runTest(testDispatcher) {
        // Given
        val fakeRepo = FakeChatMessageRepository()
        val viewModel = createViewModel(repo = fakeRepo)
        advanceUntilIdle()
        viewModel.onInputChanged("   ")

        // When
        viewModel.sendMessage()
        advanceUntilIdle()

        // Then
        assertFalse(fakeRepo.sendMessageCalled)
    }

    @Test
    fun test_sendMessage_withUmlauts_sendsCorrectBody() = runTest(testDispatcher) {
        // Given
        val fakeRepo = FakeChatMessageRepository()
        val viewModel = createViewModel(repo = fakeRepo)
        advanceUntilIdle()
        val umlautText = "Schöne Grüße aus München – äöüÄÖÜß"
        viewModel.onInputChanged(umlautText)

        // When
        viewModel.sendMessage()
        advanceUntilIdle()

        // Then
        assertTrue(fakeRepo.sendMessageCalled)
        assertEquals(umlautText, fakeRepo.lastSentBody)
    }

    @Test
    fun test_sendMessage_withEmojis_sendsCorrectBody() = runTest(testDispatcher) {
        // Given
        val fakeRepo = FakeChatMessageRepository()
        val viewModel = createViewModel(repo = fakeRepo)
        advanceUntilIdle()
        val emojiText = "Guten Morgen! 😀🎉❤️👍"
        viewModel.onInputChanged(emojiText)

        // When
        viewModel.sendMessage()
        advanceUntilIdle()

        // Then
        assertTrue(fakeRepo.sendMessageCalled)
        assertEquals(emojiText, fakeRepo.lastSentBody)
    }

    @Test
    fun test_sendMessage_withMixedUmlautsAndEmojis_sendsCorrectBody() = runTest(testDispatcher) {
        // Given
        val fakeRepo = FakeChatMessageRepository()
        val viewModel = createViewModel(repo = fakeRepo)
        advanceUntilIdle()
        val mixedText = "Schöner Tag 😀! Grüße & Küsse 💋"
        viewModel.onInputChanged(mixedText)

        // When
        viewModel.sendMessage()
        advanceUntilIdle()

        // Then
        assertTrue(fakeRepo.sendMessageCalled)
        assertEquals(mixedText, fakeRepo.lastSentBody)
    }

    @Test
    fun test_onInputChanged_withEmojis_updatesUiState() = runTest(testDispatcher) {
        // Given
        val viewModel = createViewModel()
        advanceUntilIdle()

        // When
        val emojiText = "👨‍👩‍👧‍👦 Familie 🇩🇪"
        viewModel.onInputChanged(emojiText)

        // Then
        assertEquals(emojiText, viewModel.uiState.value.inputText)
    }
}
