package com.tt.honeyDue.widget

import android.content.Context
import android.os.Build
import androidx.test.core.app.ApplicationProvider
import androidx.work.ListenableWorker
import androidx.work.testing.TestListenableWorkerBuilder
import com.tt.honeyDue.network.ApiResult
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

/**
 * Tests for [WidgetRefreshWorker] under Robolectric + WorkManager's
 * TestListenableWorkerBuilder.
 *
 * We avoid mocking the [com.tt.honeyDue.network.APILayer] singleton directly.
 * Instead the worker is parameterized by a [WidgetRefreshDataSource] that the
 * test swaps in via [WidgetRefreshWorker.dataSourceOverride].
 */
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.TIRAMISU])
class WidgetRefreshWorkerTest {

    private lateinit var context: Context
    private lateinit var repo: WidgetDataRepository

    @Before
    fun setUp() = runBlocking {
        context = ApplicationProvider.getApplicationContext()
        repo = WidgetDataRepository.get(context)
        repo.clearAll()
    }

    @After
    fun tearDown() = runBlocking {
        WidgetRefreshWorker.dataSourceOverride = null
        repo.clearAll()
    }

    private fun sampleTask(id: Long = 1L) = WidgetTaskDto(
        id = id,
        title = "Change air filter",
        priority = 2L,
        dueDate = "2026-04-20",
        isOverdue = false,
        daysUntilDue = 4,
        residenceId = 10L,
        residenceName = "Home",
        categoryIcon = "house.fill",
        completed = false
    )

    @Test
    fun worker_success_when_dataSource_returns_success() = runTest {
        val tasks = listOf(sampleTask(id = 1L), sampleTask(id = 2L))
        WidgetRefreshWorker.dataSourceOverride = FakeDataSource(
            tasksResult = ApiResult.Success(tasks),
            tier = "free"
        )

        val worker = TestListenableWorkerBuilder<WidgetRefreshWorker>(context).build()
        val result = worker.doWork()

        assertEquals(ListenableWorker.Result.success(), result)
    }

    @Test
    fun worker_persists_tasks_via_repository_on_success() = runTest {
        val tasks = listOf(
            sampleTask(id = 100L),
            sampleTask(id = 200L).copy(title = "Clean gutters")
        )
        WidgetRefreshWorker.dataSourceOverride = FakeDataSource(
            tasksResult = ApiResult.Success(tasks),
            tier = "free"
        )

        val worker = TestListenableWorkerBuilder<WidgetRefreshWorker>(context).build()
        worker.doWork()

        val stored = repo.loadTasks()
        assertEquals(2, stored.size)
        val byId = stored.associateBy { it.id }
        assertEquals("Change air filter", byId[100L]?.title)
        assertEquals("Clean gutters", byId[200L]?.title)
    }

    @Test
    fun worker_persists_tier_state_on_success() = runTest {
        WidgetRefreshWorker.dataSourceOverride = FakeDataSource(
            tasksResult = ApiResult.Success(emptyList()),
            tier = "premium"
        )

        val worker = TestListenableWorkerBuilder<WidgetRefreshWorker>(context).build()
        worker.doWork()

        assertEquals("premium", repo.loadTierState())
    }

    @Test
    fun worker_returns_retry_when_api_returns_transient_error() = runTest {
        // 500/503/timeout-class errors are retryable.
        WidgetRefreshWorker.dataSourceOverride = FakeDataSource(
            tasksResult = ApiResult.Error("server unavailable", 503),
            tier = "free"
        )

        val worker = TestListenableWorkerBuilder<WidgetRefreshWorker>(context).build()
        val result = worker.doWork()

        assertEquals(ListenableWorker.Result.retry(), result)
    }

    @Test
    fun worker_returns_retry_when_api_returns_network_error() = runTest {
        // Unknown code (e.g. network failure) → retryable.
        WidgetRefreshWorker.dataSourceOverride = FakeDataSource(
            tasksResult = ApiResult.Error("network timeout", null),
            tier = "free"
        )

        val worker = TestListenableWorkerBuilder<WidgetRefreshWorker>(context).build()
        val result = worker.doWork()

        assertEquals(ListenableWorker.Result.retry(), result)
    }

    @Test
    fun worker_returns_failure_when_api_returns_auth_error() = runTest {
        // 401 is permanent — user logged out, widget should stop trying.
        WidgetRefreshWorker.dataSourceOverride = FakeDataSource(
            tasksResult = ApiResult.Error("not authenticated", 401),
            tier = "free"
        )

        val worker = TestListenableWorkerBuilder<WidgetRefreshWorker>(context).build()
        val result = worker.doWork()

        assertEquals(ListenableWorker.Result.failure(), result)
    }

    @Test
    fun worker_does_not_clobber_tasks_on_api_failure() = runTest {
        // Pre-seed store with prior tasks.
        repo.saveTasks(listOf(sampleTask(id = 999L)))

        WidgetRefreshWorker.dataSourceOverride = FakeDataSource(
            tasksResult = ApiResult.Error("server", 503),
            tier = "free"
        )

        val worker = TestListenableWorkerBuilder<WidgetRefreshWorker>(context).build()
        worker.doWork()

        val stored = repo.loadTasks()
        assertEquals(1, stored.size)
        assertTrue(stored.any { it.id == 999L })
    }

    /** Minimal [WidgetRefreshDataSource] stub for tests. */
    private class FakeDataSource(
        private val tasksResult: ApiResult<List<WidgetTaskDto>>,
        private val tier: String
    ) : WidgetRefreshDataSource {
        override suspend fun fetchTasks(): ApiResult<List<WidgetTaskDto>> = tasksResult
        override suspend fun fetchTier(): String = tier
    }
}
