package com.garfiec.librechat.feature.auth.viewmodel

import com.google.common.truth.Truth.assertThat
import com.garfiec.librechat.core.common.result.Result
import com.garfiec.librechat.core.data.repository.AuthRepository
import com.garfiec.librechat.core.data.repository.ConfigRepository
import com.garfiec.librechat.core.model.StartupConfig
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
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.Before
import org.junit.Test

@OptIn(ExperimentalCoroutinesApi::class)
class RegisterViewModelTest {

    private val testDispatcher = StandardTestDispatcher()

    private val authRepository = mockk<AuthRepository>(relaxed = true)
    private val configRepository = mockk<ConfigRepository>(relaxed = true)

    private val configFlow = MutableStateFlow<StartupConfig?>(null)

    private lateinit var viewModel: RegisterViewModel

    @Before
    fun setup() {
        Dispatchers.setMain(testDispatcher)
        every { configRepository.startupConfig } returns configFlow
    }

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

    private fun createViewModel() = RegisterViewModel(
        authRepository = authRepository,
        configRepository = configRepository,
    )

    private fun fillValidFields() {
        viewModel.onNameChanged("Test User")
        viewModel.onEmailChanged("test@example.com")
        viewModel.onUsernameChanged("testuser")
        viewModel.onPasswordChanged("password123")
        viewModel.onConfirmPasswordChanged("password123")
    }

    @Test
    fun `initial state has empty fields`() = runTest {
        viewModel = createViewModel()
        advanceUntilIdle()

        val state = viewModel.uiState.value
        assertThat(state.name).isEmpty()
        assertThat(state.email).isEmpty()
        assertThat(state.username).isEmpty()
        assertThat(state.password).isEmpty()
        assertThat(state.confirmPassword).isEmpty()
        assertThat(state.isLoading).isFalse()
        assertThat(state.error).isNull()
        assertThat(state.isRegistered).isFalse()
    }

    @Test
    fun `field changes update state and clear error`() = runTest {
        viewModel = createViewModel()
        advanceUntilIdle()

        viewModel.onNameChanged("John")
        assertThat(viewModel.uiState.value.name).isEqualTo("John")

        viewModel.onEmailChanged("john@test.com")
        assertThat(viewModel.uiState.value.email).isEqualTo("john@test.com")

        viewModel.onUsernameChanged("johndoe")
        assertThat(viewModel.uiState.value.username).isEqualTo("johndoe")

        viewModel.onPasswordChanged("pass123")
        assertThat(viewModel.uiState.value.password).isEqualTo("pass123")

        viewModel.onConfirmPasswordChanged("pass123")
        assertThat(viewModel.uiState.value.confirmPassword).isEqualTo("pass123")
    }

    @Test
    fun `register with blank fields shows error`() = runTest {
        viewModel = createViewModel()
        advanceUntilIdle()

        viewModel.register()
        advanceUntilIdle()

        assertThat(viewModel.uiState.value.error).isEqualTo("All fields are required")
    }

    @Test
    fun `register with blank name shows error`() = runTest {
        viewModel = createViewModel()
        advanceUntilIdle()

        viewModel.onEmailChanged("test@test.com")
        viewModel.onUsernameChanged("testuser")
        viewModel.onPasswordChanged("password123")
        viewModel.onConfirmPasswordChanged("password123")
        viewModel.register()
        advanceUntilIdle()

        assertThat(viewModel.uiState.value.error).isEqualTo("All fields are required")
    }

    @Test
    fun `register with short password shows error`() = runTest {
        viewModel = createViewModel()
        advanceUntilIdle()

        viewModel.onNameChanged("Test")
        viewModel.onEmailChanged("test@test.com")
        viewModel.onUsernameChanged("testuser")
        viewModel.onPasswordChanged("short")
        viewModel.onConfirmPasswordChanged("short")
        viewModel.register()
        advanceUntilIdle()

        assertThat(viewModel.uiState.value.error).isEqualTo("Password must be at least 8 characters")
    }

    @Test
    fun `register with password mismatch shows error`() = runTest {
        viewModel = createViewModel()
        advanceUntilIdle()

        viewModel.onNameChanged("Test")
        viewModel.onEmailChanged("test@test.com")
        viewModel.onUsernameChanged("testuser")
        viewModel.onPasswordChanged("password123")
        viewModel.onConfirmPasswordChanged("different456")
        viewModel.register()
        advanceUntilIdle()

        assertThat(viewModel.uiState.value.error).isEqualTo("Passwords do not match")
    }

    @Test
    fun `successful registration sets isRegistered true`() = runTest {
        coEvery {
            authRepository.register("Test User", "test@example.com", "testuser", "password123")
        } returns Result.Success(Unit)

        viewModel = createViewModel()
        advanceUntilIdle()

        fillValidFields()
        viewModel.register()
        advanceUntilIdle()

        val state = viewModel.uiState.value
        assertThat(state.isRegistered).isTrue()
        assertThat(state.isLoading).isFalse()
        assertThat(state.error).isNull()
    }

    @Test
    fun `registration failure shows error message`() = runTest {
        coEvery {
            authRepository.register(any(), any(), any(), any())
        } returns Result.Error(message = "Email already exists")

        viewModel = createViewModel()
        advanceUntilIdle()

        fillValidFields()
        viewModel.register()
        advanceUntilIdle()

        val state = viewModel.uiState.value
        assertThat(state.error).isEqualTo("Email already exists")
        assertThat(state.isRegistered).isFalse()
        assertThat(state.isLoading).isFalse()
    }

    @Test
    fun `registration failure with null message uses default`() = runTest {
        coEvery {
            authRepository.register(any(), any(), any(), any())
        } returns Result.Error(message = null)

        viewModel = createViewModel()
        advanceUntilIdle()

        fillValidFields()
        viewModel.register()
        advanceUntilIdle()

        assertThat(viewModel.uiState.value.error).isEqualTo("Registration failed")
    }

    @Test
    fun `startup config updates minPasswordLength`() = runTest {
        viewModel = createViewModel()
        advanceUntilIdle()

        configFlow.value = StartupConfig(minPasswordLength = 12)
        advanceUntilIdle()

        assertThat(viewModel.uiState.value.minPasswordLength).isEqualTo(12)
    }

    @Test
    fun `custom minPasswordLength is enforced during validation`() = runTest {
        viewModel = createViewModel()
        advanceUntilIdle()

        configFlow.value = StartupConfig(minPasswordLength = 12)
        advanceUntilIdle()

        viewModel.onNameChanged("Test")
        viewModel.onEmailChanged("test@test.com")
        viewModel.onUsernameChanged("testuser")
        viewModel.onPasswordChanged("short1234") // 9 chars, less than 12
        viewModel.onConfirmPasswordChanged("short1234")
        viewModel.register()
        advanceUntilIdle()

        assertThat(viewModel.uiState.value.error).isEqualTo("Password must be at least 12 characters")
    }

    @Test
    fun `register calls authRepository with correct parameters`() = runTest {
        coEvery { authRepository.register(any(), any(), any(), any()) } returns Result.Success(Unit)

        viewModel = createViewModel()
        advanceUntilIdle()

        fillValidFields()
        viewModel.register()
        advanceUntilIdle()

        coVerify {
            authRepository.register("Test User", "test@example.com", "testuser", "password123")
        }
    }

    @Test
    fun `changing field after error clears the error`() = runTest {
        viewModel = createViewModel()
        advanceUntilIdle()

        viewModel.register() // triggers "All fields are required"
        advanceUntilIdle()
        assertThat(viewModel.uiState.value.error).isNotNull()

        viewModel.onNameChanged("Test")
        assertThat(viewModel.uiState.value.error).isNull()
    }
}
