package de.krisenvorrat.app.ui.messaging

import de.krisenvorrat.app.data.db.entity.MessageEntity
import de.krisenvorrat.app.domain.model.SyncError
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.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test

private class FakeUserListMessageRepository(
    private val usersResult: Result<List<UserListItemDto>>
) : MessageRepository {
    var fetchCount = 0

    override fun getConversation(myId: String, otherId: String): Flow<List<MessageEntity>> =
        MutableStateFlow(emptyList())
    override suspend fun sendMessage(recipientId: String, body: String) = Unit
    override suspend fun fetchUsers(): Result<List<UserListItemDto>> {
        fetchCount++
        return usersResult
    }
    override suspend fun getMyUserId(): String = ""
    override suspend fun drainPendingMessages() = Unit
}

@OptIn(ExperimentalCoroutinesApi::class)
class UserListViewModelTest {

    private val testDispatcher = StandardTestDispatcher()

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

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

    @Test
    fun test_loadUsers_success_showsUsers() = runTest(testDispatcher) {
        // Given
        val users = listOf(UserListItemDto("u1", "Alice"), UserListItemDto("u2", "Bob"))
        val repo = FakeUserListMessageRepository(Result.success(users))
        val viewModel = UserListViewModel(repo)

        // When
        advanceUntilIdle()

        // Then
        assertEquals(2, viewModel.uiState.value.users.size)
        assertFalse(viewModel.uiState.value.isLoading)
        assertNull(viewModel.uiState.value.errorMessage)
    }

    @Test
    fun test_loadUsers_failure_showsError() = runTest(testDispatcher) {
        // Given
        val repo = FakeUserListMessageRepository(
            Result.failure(SyncError.ConnectionError())
        )
        val viewModel = UserListViewModel(repo)

        // When
        advanceUntilIdle()

        // Then
        assertFalse(viewModel.uiState.value.isLoading)
        assertNotNull(viewModel.uiState.value.errorMessage)
        assertTrue(viewModel.uiState.value.users.isEmpty())
    }

    @Test
    fun test_retry_reloadsUsers() = runTest(testDispatcher) {
        // Given
        val repo = FakeUserListMessageRepository(Result.success(emptyList()))
        val viewModel = UserListViewModel(repo)
        advanceUntilIdle()
        val countBefore = repo.fetchCount

        // When
        viewModel.retry()
        advanceUntilIdle()

        // Then
        assertEquals(countBefore + 1, repo.fetchCount)
    }
}
