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

import com.tt.honeyDue.models.TaskCategory
import com.tt.honeyDue.models.TaskCreateRequest
import com.tt.honeyDue.models.TaskResponse
import com.tt.honeyDue.models.TaskSuggestionResponse
import com.tt.honeyDue.models.TaskSuggestionsResponse
import com.tt.honeyDue.models.TaskTemplate
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.assertIs
import kotlin.test.assertNull
import kotlin.test.assertTrue

/**
 * Unit tests for TaskSuggestionsViewModel covering:
 *  1. Initial state is Idle.
 *  2. load() transitions to Success with suggestions.
 *  3. Empty suggestions -> Success with empty list (empty state UI).
 *  4. Accept fires createTask with exact template fields + templateId backlink.
 *  5. Accept success fires non-onboarding analytics task_suggestion_accepted.
 *  6. Load error surfaces ApiResult.Error + retry() reloads endpoint.
 *  7. ViewModel is constructible with an explicit residenceId (standalone path).
 *  8. Accept error surfaces error and fires no analytics.
 *  9. resetAcceptState returns to Idle.
 */
@OptIn(ExperimentalCoroutinesApi::class)
class TaskSuggestionsViewModelTest {

    private val dispatcher = StandardTestDispatcher()

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

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

    private val plumbingCat = TaskCategory(id = 1, name = "Plumbing")

    private val template1 = TaskTemplate(
        id = 100,
        title = "Change Water Filter",
        description = "Replace every 6 months.",
        categoryId = 1,
        category = plumbingCat,
        frequencyId = 7
    )
    private val template2 = TaskTemplate(
        id = 200,
        title = "Flush Water Heater",
        description = "Annual flush.",
        categoryId = 1,
        category = plumbingCat,
        frequencyId = 8
    )

    private val suggestionsResponse = TaskSuggestionsResponse(
        suggestions = listOf(
            TaskSuggestionResponse(
                template = template1,
                relevanceScore = 0.92,
                matchReasons = listOf("Has water heater")
            ),
            TaskSuggestionResponse(
                template = template2,
                relevanceScore = 0.75,
                matchReasons = listOf("Annual maintenance")
            )
        ),
        totalCount = 2,
        profileCompleteness = 0.8
    )

    private val emptyResponse = TaskSuggestionsResponse(
        suggestions = emptyList(),
        totalCount = 0,
        profileCompleteness = 0.5
    )

    private fun fakeCreatedTask(templateId: Int?): TaskResponse = TaskResponse(
        id = 777,
        residenceId = 42,
        createdById = 1,
        title = "Created",
        description = "",
        categoryId = 1,
        frequencyId = 7,
        templateId = templateId,
        createdAt = "2024-01-01T00:00:00Z",
        updatedAt = "2024-01-01T00:00:00Z"
    )

    private fun makeViewModel(
        loadResult: ApiResult<TaskSuggestionsResponse> = ApiResult.Success(suggestionsResponse),
        createResult: ApiResult<TaskResponse> = ApiResult.Success(fakeCreatedTask(100)),
        onCreateCall: (TaskCreateRequest) -> Unit = {},
        onAnalytics: (String, Map<String, Any>) -> Unit = { _, _ -> },
        residenceId: Int = 42
    ) = TaskSuggestionsViewModel(
        residenceId = residenceId,
        loadSuggestions = { loadResult },
        createTask = { request ->
            onCreateCall(request)
            createResult
        },
        analytics = onAnalytics
    )

    @Test
    fun initialStateIsIdle() {
        val vm = makeViewModel()
        assertIs<ApiResult.Idle>(vm.suggestionsState.value)
        assertIs<ApiResult.Idle>(vm.acceptState.value)
    }

    @Test
    fun loadTransitionsToSuccessWithSuggestions() = runTest(dispatcher) {
        val vm = makeViewModel()
        vm.load()
        dispatcher.scheduler.advanceUntilIdle()

        val state = vm.suggestionsState.value
        assertIs<ApiResult.Success<TaskSuggestionsResponse>>(state)
        assertEquals(2, state.data.totalCount)
        assertEquals(100, state.data.suggestions.first().template.id)
    }

    @Test
    fun emptySuggestionsResolvesToSuccessWithEmptyList() = runTest(dispatcher) {
        val vm = makeViewModel(loadResult = ApiResult.Success(emptyResponse))
        vm.load()
        dispatcher.scheduler.advanceUntilIdle()

        val state = vm.suggestionsState.value
        assertIs<ApiResult.Success<TaskSuggestionsResponse>>(state)
        assertTrue(state.data.suggestions.isEmpty())
        assertEquals(0, state.data.totalCount)
    }

    @Test
    fun acceptInvokesCreateTaskWithTemplateIdBacklinkAndFields() = runTest(dispatcher) {
        var captured: TaskCreateRequest? = null
        val vm = makeViewModel(onCreateCall = { captured = it })
        vm.load()
        dispatcher.scheduler.advanceUntilIdle()

        vm.accept(suggestionsResponse.suggestions.first())
        dispatcher.scheduler.advanceUntilIdle()

        val req = captured ?: error("createTask was not called")
        assertEquals(42, req.residenceId)
        assertEquals("Change Water Filter", req.title)
        assertEquals("Replace every 6 months.", req.description)
        assertEquals(1, req.categoryId)
        assertEquals(7, req.frequencyId)
        assertEquals(100, req.templateId)
    }

    @Test
    fun acceptSuccessFiresNonOnboardingAnalyticsEvent() = runTest(dispatcher) {
        val events = mutableListOf<Pair<String, Map<String, Any>>>()
        val vm = makeViewModel(onAnalytics = { name, props -> events += name to props })
        vm.load()
        dispatcher.scheduler.advanceUntilIdle()

        vm.accept(suggestionsResponse.suggestions.first())
        dispatcher.scheduler.advanceUntilIdle()

        val accepted = events.firstOrNull {
            it.first == TaskSuggestionsViewModel.EVENT_TASK_SUGGESTION_ACCEPTED
        }
        assertTrue(accepted != null, "expected task_suggestion_accepted event")
        assertEquals(100, accepted.second["template_id"])
        assertEquals(0.92, accepted.second["relevance_score"])

        assertTrue(events.none { it.first == "onboarding_suggestion_accepted" })

        val state = vm.acceptState.value
        assertIs<ApiResult.Success<TaskResponse>>(state)
    }

    @Test
    fun loadErrorSurfacesErrorAndRetryReloads() = runTest(dispatcher) {
        var callCount = 0
        var nextResult: ApiResult<TaskSuggestionsResponse> = ApiResult.Error("Network down", 500)
        val vm = TaskSuggestionsViewModel(
            residenceId = 42,
            loadSuggestions = {
                callCount++
                nextResult
            },
            createTask = { ApiResult.Success(fakeCreatedTask(null)) },
            analytics = { _, _ -> }
        )

        vm.load()
        dispatcher.scheduler.advanceUntilIdle()
        assertIs<ApiResult.Error>(vm.suggestionsState.value)
        assertEquals(1, callCount)

        nextResult = ApiResult.Success(suggestionsResponse)
        vm.retry()
        dispatcher.scheduler.advanceUntilIdle()

        assertEquals(2, callCount)
        assertIs<ApiResult.Success<TaskSuggestionsResponse>>(vm.suggestionsState.value)
    }

    @Test
    fun viewModelUsesProvidedResidenceIdOnStandalonePath() = runTest(dispatcher) {
        var captured: TaskCreateRequest? = null
        val vm = makeViewModel(
            residenceId = 999,
            onCreateCall = { captured = it }
        )
        vm.load()
        dispatcher.scheduler.advanceUntilIdle()
        vm.accept(suggestionsResponse.suggestions.first())
        dispatcher.scheduler.advanceUntilIdle()

        assertEquals(999, captured?.residenceId)
    }

    @Test
    fun acceptErrorSurfacesErrorAndFiresNoAnalytics() = runTest(dispatcher) {
        val events = mutableListOf<Pair<String, Map<String, Any>>>()
        val vm = makeViewModel(
            createResult = ApiResult.Error("Server error", 500),
            onAnalytics = { name, props -> events += name to props }
        )
        vm.load()
        dispatcher.scheduler.advanceUntilIdle()

        vm.accept(suggestionsResponse.suggestions.first())
        dispatcher.scheduler.advanceUntilIdle()

        val state = vm.acceptState.value
        assertIs<ApiResult.Error>(state)
        assertEquals("Server error", state.message)
        assertTrue(events.isEmpty(), "analytics should not fire on accept error")
    }

    @Test
    fun resetAcceptStateReturnsToIdle() = runTest(dispatcher) {
        val vm = makeViewModel(createResult = ApiResult.Error("boom", 500))
        vm.load()
        dispatcher.scheduler.advanceUntilIdle()
        vm.accept(suggestionsResponse.suggestions.first())
        dispatcher.scheduler.advanceUntilIdle()
        assertIs<ApiResult.Error>(vm.acceptState.value)

        vm.resetAcceptState()
        assertIs<ApiResult.Idle>(vm.acceptState.value)
        assertNull(vm.lastAcceptedTemplateId)
    }
}
