package com.correx.apps.server.workspace

import com.correx.core.kernel.orchestration.WorkspaceContext
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.nio.file.Files
import java.nio.file.Path
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlin.test.assertTrue

class WorkspaceResolverTest {

    private fun bootDefault(root: Path) = WorkspaceContext(
        workspaceRoot = root,
        workingDir = root,
        allowedPaths = setOf(root),
    )

    @Test
    fun `null clientDir returns Bound with bootDefault`(@TempDir base: Path) {
        val default = bootDefault(base)
        val resolver = WorkspaceResolver(default, emptyList(), emptyList())

        val result = resolver.resolve(null)

        assertIs<WorkspaceResolution.Bound>(result)
        assertEquals(default, result.workspace)
    }

    @Test
    fun `existing non-privileged dir with empty allow-list returns Bound`(@TempDir base: Path) {
        val clientDir = Files.createDirectory(base.resolve("project"))
        val canonical = clientDir.toRealPath()
        val default = bootDefault(base)
        val resolver = WorkspaceResolver(default, emptyList(), emptyList())

        val result = resolver.resolve(clientDir.toString())

        assertIs<WorkspaceResolution.Bound>(result)
        assertEquals(canonical, result.workspace.workspaceRoot)
        assertEquals(canonical, result.workspace.workingDir)
        assertEquals(setOf(canonical), result.workspace.allowedPaths)
    }

    @Test
    fun `nonexistent path returns Rejected with reason mentioning does not exist`(@TempDir base: Path) {
        val missing = base.resolve("does-not-exist")
        val default = bootDefault(base)
        val resolver = WorkspaceResolver(default, emptyList(), emptyList())

        val result = resolver.resolve(missing.toString())

        assertIs<WorkspaceResolution.Rejected>(result)
        assertTrue(result.reason.contains("does not exist"), "reason was: ${result.reason}")
        assertEquals(default, result.fallback)
    }

    @Test
    fun `file path returns Rejected with reason mentioning not a directory`(@TempDir base: Path) {
        val file = Files.createFile(base.resolve("regular-file.txt"))
        val default = bootDefault(base)
        val resolver = WorkspaceResolver(default, emptyList(), emptyList())

        val result = resolver.resolve(file.toString())

        assertIs<WorkspaceResolution.Rejected>(result)
        assertTrue(result.reason.contains("not a directory"), "reason was: ${result.reason}")
        assertEquals(default, result.fallback)
    }

    @Test
    fun `path under privileged location returns Rejected mentioning privileged`(@TempDir base: Path) {
        val privilegedRoot = Files.createDirectory(base.resolve("system"))
        val clientDir = Files.createDirectory(privilegedRoot.resolve("secret"))
        val default = bootDefault(base)
        val resolver = WorkspaceResolver(default, listOf(privilegedRoot), emptyList())

        val result = resolver.resolve(clientDir.toString())

        assertIs<WorkspaceResolution.Rejected>(result)
        assertTrue(result.reason.contains("privileged"), "reason was: ${result.reason}")
        assertEquals(default, result.fallback)
    }

    @Test
    fun `symlink inside allowed area pointing to privileged dir is Rejected`(@TempDir base: Path) {
        val privilegedRoot = Files.createDirectory(base.resolve("privileged"))
        val allowedRoot = Files.createDirectory(base.resolve("allowed"))
        val link = allowedRoot.resolve("escape-link")
        Files.createSymbolicLink(link, privilegedRoot)
        val default = bootDefault(base)
        val resolver = WorkspaceResolver(default, listOf(privilegedRoot), listOf(allowedRoot))

        val result = resolver.resolve(link.toString())

        assertIs<WorkspaceResolution.Rejected>(result)
        assertTrue(result.reason.contains("privileged"), "reason was: ${result.reason}")
    }

    @Test
    fun `path under an allowed root with non-empty allow-list returns Bound`(@TempDir base: Path) {
        val allowedRoot = Files.createDirectory(base.resolve("projects"))
        val clientDir = Files.createDirectory(allowedRoot.resolve("my-app"))
        val default = bootDefault(base)
        val resolver = WorkspaceResolver(default, emptyList(), listOf(allowedRoot))

        val result = resolver.resolve(clientDir.toString())

        assertIs<WorkspaceResolution.Bound>(result)
        assertEquals(clientDir.toRealPath(), result.workspace.workspaceRoot)
    }

    @Test
    fun `path outside all allowed roots returns Rejected mentioning not under an allowed root`(@TempDir base: Path) {
        val allowedRoot = Files.createDirectory(base.resolve("projects"))
        val outsideDir = Files.createDirectory(base.resolve("outside"))
        val default = bootDefault(base)
        val resolver = WorkspaceResolver(default, emptyList(), listOf(allowedRoot))

        val result = resolver.resolve(outsideDir.toString())

        assertIs<WorkspaceResolution.Rejected>(result)
        assertTrue(result.reason.contains("not under an allowed root"), "reason was: ${result.reason}")
        assertEquals(default, result.fallback)
    }

    @Test
    fun `privilegedLocations are propagated to bound WorkspaceContext`(@TempDir base: Path) {
        val privileged = listOf(base.resolve("sys"))
        val clientDir = Files.createDirectory(base.resolve("project"))
        val default = bootDefault(base)
        val resolver = WorkspaceResolver(default, privileged, emptyList())

        val result = resolver.resolve(clientDir.toString())

        assertIs<WorkspaceResolution.Bound>(result)
        assertEquals(privileged, result.workspace.privilegedLocations)
    }
}
