package com.tt.honeyDue.ui.screens.task

import com.tt.honeyDue.models.TaskCreateRequest
import com.tt.honeyDue.models.TaskResponse
import com.tt.honeyDue.network.ApiResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertIs
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

/**
 * Unit tests for [AddTaskWithResidenceViewModel] — the state-logic layer
 * behind AddTaskWithResidenceScreen (P2 Stream I). Covers validation,
 * submit -> APILayer.createTask wiring, residenceId pre-selection, and
 * success/error outcomes.
 */
@OptIn(ExperimentalCoroutinesApi::class)
class AddTaskWithResidenceViewModelTest {

    private val dispatcher = StandardTestDispatcher()

    @BeforeTest
    fun setUp() { Dispatchers.setMain(dispatcher) }

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

    private fun fakeCreatedTask(): TaskResponse = TaskResponse(
        id = 1,
        residenceId = 42,
        createdById = 1,
        title = "Created",
        description = "",
        createdAt = "2024-01-01T00:00:00Z",
        updatedAt = "2024-01-01T00:00:00Z"
    )

    private fun makeViewModel(
        residenceId: Int = 42,
        createResult: ApiResult<TaskResponse> = ApiResult.Success(fakeCreatedTask()),
        onCreateCall: (TaskCreateRequest) -> Unit = {}
    ) = AddTaskWithResidenceViewModel(
        residenceId = residenceId,
        createTask = { request ->
            onCreateCall(request)
            createResult
        }
    )

    @Test
    fun titleEmpty_submitDisabled() {
        val vm = makeViewModel()
        assertTrue(vm.title.value.isEmpty())
        assertFalse(vm.canSubmit.value, "submit should be disabled with empty title")
    }

    @Test
    fun titleValid_submitEnabled() {
        val vm = makeViewModel()
        vm.onTitleChange("Change water filter")
        assertTrue(vm.canSubmit.value, "submit should be enabled when title is non-empty")
    }

    @Test
    fun titleWhitespaceOnly_submitDisabled() {
        val vm = makeViewModel()
        vm.onTitleChange("   ")
        assertFalse(vm.canSubmit.value, "whitespace-only title should not enable submit")
    }

    @Test
    fun submit_buildsTaskCreateRequestWithResidenceId() = runTest(dispatcher) {
        var captured: TaskCreateRequest? = null
        val vm = makeViewModel(
            residenceId = 42,
            onCreateCall = { captured = it }
        )
        vm.onTitleChange("Flush water heater")
        vm.onDescriptionChange("Annual flush")
        vm.onCategoryIdChange(3)
        vm.onFrequencyIdChange(7)
        vm.onPriorityIdChange(2)
        vm.onDueDateChange("2024-06-15")
        vm.onEstimatedCostChange("150.50")
        vm.submit(onSuccess = {})
        dispatcher.scheduler.advanceUntilIdle()

        val req = assertNotNull(captured, "createTask was not called")
        assertEquals(42, req.residenceId)
        assertEquals("Flush water heater", req.title)
        assertEquals("Annual flush", req.description)
        assertEquals(3, req.categoryId)
        assertEquals(7, req.frequencyId)
        assertEquals(2, req.priorityId)
        assertEquals("2024-06-15", req.dueDate)
        assertEquals(150.50, req.estimatedCost)
    }

    @Test
    fun submit_success_invokesOnCreated() = runTest(dispatcher) {
        var createdCalled = 0
        val vm = makeViewModel(createResult = ApiResult.Success(fakeCreatedTask()))
        vm.onTitleChange("Clean gutters")
        vm.submit(onSuccess = { createdCalled++ })
        dispatcher.scheduler.advanceUntilIdle()

        assertEquals(1, createdCalled, "onCreated should fire exactly once on success")
        assertIs<ApiResult.Success<TaskResponse>>(vm.submitState.value)
    }

    @Test
    fun submit_failure_surfacesError() = runTest(dispatcher) {
        var createdCalled = 0
        val vm = makeViewModel(createResult = ApiResult.Error("Server exploded", 500))
        vm.onTitleChange("Mow lawn")
        vm.submit(onSuccess = { createdCalled++ })
        dispatcher.scheduler.advanceUntilIdle()

        val state = vm.submitState.value
        assertIs<ApiResult.Error>(state)
        assertEquals("Server exploded", state.message)
        assertEquals(0, createdCalled, "onCreated must NOT fire on API error")
    }

    @Test
    fun residenceId_passedIntoRequest() = runTest(dispatcher) {
        var captured: TaskCreateRequest? = null
        val vm = makeViewModel(
            residenceId = 999,
            onCreateCall = { captured = it }
        )
        vm.onTitleChange("Replace batteries")
        vm.submit(onSuccess = {})
        dispatcher.scheduler.advanceUntilIdle()

        assertEquals(999, captured?.residenceId)
    }

    @Test
    fun submit_emptyTitle_doesNotCallCreateTask() = runTest(dispatcher) {
        var callCount = 0
        val vm = makeViewModel(onCreateCall = { callCount++ })
        // Title intentionally left blank.
        vm.submit(onSuccess = {})
        dispatcher.scheduler.advanceUntilIdle()

        assertEquals(0, callCount, "createTask must not be called when title is blank")
        assertEquals("Title is required", vm.titleError.value)
    }

    @Test
    fun submit_omitsOptionalFieldsWhenBlank() = runTest(dispatcher) {
        var captured: TaskCreateRequest? = null
        val vm = makeViewModel(onCreateCall = { captured = it })
        vm.onTitleChange("Just a title")
        // Leave description, dueDate, estimatedCost blank.
        vm.submit(onSuccess = {})
        dispatcher.scheduler.advanceUntilIdle()

        val req = assertNotNull(captured)
        assertNull(req.description, "blank description should serialize as null")
        assertNull(req.dueDate, "blank dueDate should serialize as null")
        assertNull(req.estimatedCost, "blank estimatedCost should serialize as null")
    }
}
