package wang.yaojia.webterm.viewmodels

import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import wang.yaojia.webterm.api.models.CommitLogEntry
import wang.yaojia.webterm.api.models.GitLogResult
import wang.yaojia.webterm.api.models.PrAvailability
import wang.yaojia.webterm.api.models.PrStatus
import wang.yaojia.webterm.api.models.ProjectDetail

/**
 * W5 ProjectDetailViewModel side fetches (JVM). The PR chip and recent-commits list are failure-
 * ISOLATED: a failure of either NEVER fails the detail load nor the other; a non-`ok` availability
 * renders a degraded (but Loaded) chip; the commit list decodes into its own state.
 */
class ProjectDetailPrLogTest {

    private val detail = ProjectDetail(name = "repo", path = "/repo", isGit = true, branch = "main")

    @Test
    fun `detail plus PR plus log all load`() = runTest {
        val gateway = FakeWorktreeGateway(
            detail = detail,
            prResult = PrStatus(availability = PrAvailability.OK, number = 7, title = "A PR"),
            logResult = GitLogResult(commits = listOf(CommitLogEntry("h", 1, "s")), truncated = false),
        )
        val vm = ProjectDetailViewModel.forGateway(gateway, "/repo")

        vm.load()

        assertTrue(vm.phase.value is ProjectDetailViewModel.Phase.Loaded)
        val chip = vm.prChip.value as ProjectDetailViewModel.PrChip.Loaded
        assertEquals(PrAvailability.OK, chip.status.availability)
        val commits = vm.recentCommits.value as ProjectDetailViewModel.RecentCommits.Loaded
        assertEquals(1, commits.result.commits.size)
    }

    @Test
    fun `a PR fetch failure does not fail the detail load nor the log`() = runTest {
        val gateway = FakeWorktreeGateway(
            detail = detail,
            prThrows = true,
            logResult = GitLogResult(commits = emptyList(), truncated = false),
        )
        val vm = ProjectDetailViewModel.forGateway(gateway, "/repo")

        vm.load()

        assertTrue(vm.phase.value is ProjectDetailViewModel.Phase.Loaded, "detail must still load")
        assertEquals(ProjectDetailViewModel.PrChip.Unavailable, vm.prChip.value)
        assertTrue(vm.recentCommits.value is ProjectDetailViewModel.RecentCommits.Loaded, "log stays isolated")
    }

    @Test
    fun `a log fetch failure isolates to the recent-commits state only`() = runTest {
        val gateway = FakeWorktreeGateway(
            detail = detail,
            prResult = PrStatus(availability = PrAvailability.NO_PR),
            logThrows = true,
        )
        val vm = ProjectDetailViewModel.forGateway(gateway, "/repo")

        vm.load()

        assertTrue(vm.phase.value is ProjectDetailViewModel.Phase.Loaded)
        assertEquals(ProjectDetailViewModel.RecentCommits.Unavailable, vm.recentCommits.value)
        // A non-ok availability is still a Loaded chip (degraded copy is a render concern).
        val chip = vm.prChip.value as ProjectDetailViewModel.PrChip.Loaded
        assertEquals(PrAvailability.NO_PR, chip.status.availability)
    }

    @Test
    fun `the wired worktree VM shares the repo path and refreshes the detail on a successful create`() = runTest {
        val gateway = FakeWorktreeGateway(
            detail = detail,
            prResult = PrStatus(availability = PrAvailability.DISABLED),
            logResult = GitLogResult(),
        )
        val vm = ProjectDetailViewModel.forGateway(gateway, "/repo")
        vm.load()
        val detailCallsAfterLoad = gateway.detailCalls

        vm.worktree!!.create("feat/x")

        assertTrue(gateway.detailCalls > detailCallsAfterLoad, "create success re-fetches the detail")
        assertEquals("/repo", gateway.createCalls.single().first)
    }
}
