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.CreateWorktreeResult
import wang.yaojia.webterm.api.models.GitWriteOutcome
import wang.yaojia.webterm.api.models.PruneWorktreesResult
import wang.yaojia.webterm.api.models.RemoveWorktreeResult
import wang.yaojia.webterm.api.models.WorktreeInfo

/**
 * W5 WorktreeViewModel (JVM). The guarded worktree write phase machine: client-side branch validation
 * (no I/O on a bad name), main-worktree removal blocked client-side, the force flag threaded, and the
 * server's SAFE error strings (disabled 403 / 429) surfaced inertly. On success it re-fetches the detail.
 */
class WorktreeViewModelTest {

    @Test
    fun `an invalid branch name fails with no network I O`() = runTest {
        val gateway = FakeWorktreeGateway()
        val vm = WorktreeViewModel(gateway, "/repo")

        vm.create("bad branch~name") // whitespace + '~' are forbidden

        assertTrue(vm.phase.value is WorktreeViewModel.Phase.Failed)
        assertEquals(WorktreeCopy.INVALID_BRANCH, (vm.phase.value as WorktreeViewModel.Phase.Failed).message)
        assertEquals(0, gateway.createCalls.size, "an invalid branch must never hit the network")
    }

    @Test
    fun `a leading dash and dotdot and trailing dot are all rejected client-side`() {
        assertTrue(WorktreeViewModel.isValidBranchName("feat/ok-name"))
        assertTrue(WorktreeViewModel.isValidBranchName("release/1.2.x"))
        listOf("-flag", "a..b", "ends.", "has space", "a~b", "a:b", "@{now}", "", "//x", "/lead", "trail/").forEach {
            assertTrue(!WorktreeViewModel.isValidBranchName(it), "should reject '$it'")
        }
    }

    @Test
    fun `create success settles Done and re-fetches the detail`() = runTest {
        val gateway = FakeWorktreeGateway(
            createOutcome = GitWriteOutcome.Ok(CreateWorktreeResult(path = "/repo-worktrees/feat", branch = "feat/x")),
        )
        var refreshes = 0
        val vm = WorktreeViewModel(gateway, "/repo", onChanged = { refreshes++ })

        vm.create("feat/x", base = "main")

        assertTrue(vm.phase.value is WorktreeViewModel.Phase.Done)
        assertEquals(1, refreshes, "a successful create must re-fetch the detail")
        assertEquals(Triple("/repo", "feat/x", "main"), gateway.createCalls.single())
    }

    @Test
    fun `removing a main worktree is blocked client-side with no I O`() = runTest {
        val gateway = FakeWorktreeGateway()
        val vm = WorktreeViewModel(gateway, "/repo")

        vm.remove(WorktreeInfo(path = "/repo", branch = "main", isMain = true), force = false)

        assertEquals(WorktreeCopy.CANNOT_REMOVE_MAIN, (vm.phase.value as WorktreeViewModel.Phase.Failed).message)
        assertEquals(0, gateway.removeCalls.size)
    }

    @Test
    fun `remove threads the force flag`() = runTest {
        val gateway = FakeWorktreeGateway(removeOutcome = GitWriteOutcome.Ok(RemoveWorktreeResult(path = "/wt/x")))
        val vm = WorktreeViewModel(gateway, "/repo")

        vm.remove(WorktreeInfo(path = "/wt/x", branch = "feat", isMain = false), force = true)

        assertTrue(vm.phase.value is WorktreeViewModel.Phase.Done)
        assertEquals(Triple("/repo", "/wt/x", true), gateway.removeCalls.single())
    }

    @Test
    fun `a 403 disabled rejection surfaces the safe server message inertly`() = runTest {
        val gateway = FakeWorktreeGateway(
            createOutcome = GitWriteOutcome.Rejected(403, "Worktree creation is disabled."),
        )
        val vm = WorktreeViewModel(gateway, "/repo")

        vm.create("feat/x")

        assertEquals("Worktree creation is disabled.", (vm.phase.value as WorktreeViewModel.Phase.Failed).message)
    }

    @Test
    fun `a 429 rate-limit surfaces the rate-limited copy`() = runTest {
        val gateway = FakeWorktreeGateway(pruneOutcome = GitWriteOutcome.RateLimited)
        val vm = WorktreeViewModel(gateway, "/repo")

        vm.prune()

        assertEquals(WorktreeCopy.RATE_LIMITED, (vm.phase.value as WorktreeViewModel.Phase.Failed).message)
    }

    @Test
    fun `prune with nothing to reclaim reports an empty result`() = runTest {
        val gateway = FakeWorktreeGateway(pruneOutcome = GitWriteOutcome.Ok(PruneWorktreesResult(pruned = emptyList())))
        val vm = WorktreeViewModel(gateway, "/repo")

        vm.prune()

        val done = vm.phase.value as WorktreeViewModel.Phase.Done
        assertTrue(done.message.contains("没有"))
    }
}
