package com.correx.core.toolintent

import com.correx.core.events.events.ToolRequest
import com.correx.core.events.risk.RiskAction
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import com.correx.core.events.types.ToolInvocationId
import com.correx.core.toolintent.rules.ReferenceExistsRule
import com.correx.core.tools.contract.ToolCapability
import java.nio.file.Path
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class ReferenceExistsRuleTest {

    private val workspace = Path.of("/work/project")
    private val rule = ReferenceExistsRule()

    private class FakeProbe(private val existing: Set<Path> = emptySet()) : WorldProbe {
        override fun exists(path: Path): Boolean = path.toAbsolutePath().normalize() in existing
        override fun resolveReal(path: Path): Path = path.toAbsolutePath().normalize()
    }

    private fun abs(p: String): Path = Path.of(p).toAbsolutePath().normalize()

    private fun input(pathArg: String, probe: WorldProbe) = ToolCallAssessmentInput(
        request = ToolRequest(
            ToolInvocationId("i"),
            SessionId("s"),
            StageId("st"),
            "file_read",
            mapOf("path" to pathArg),
        ),
        capabilities = setOf(ToolCapability.FILE_READ),
        workspace = WorkspacePolicy(workspace, emptyList()),
        probe = probe,
    )

    @Test
    fun `applies only to FILE_READ`() {
        assertTrue(rule.appliesTo(setOf(ToolCapability.FILE_READ)))
        assertFalse(rule.appliesTo(setOf(ToolCapability.FILE_WRITE)))
    }

    @Test
    fun `directory-enumeration tools are exempt`() {
        // list_dir carries FILE_READ + DIRECTORY_LIST; listing a not-yet-created dir is a legitimate
        // survey move, so the anti-hallucination read gate must not block it.
        assertFalse(rule.appliesTo(setOf(ToolCapability.FILE_READ, ToolCapability.DIRECTORY_LIST)))
    }

    @Test
    fun `reading a non-existent in-workspace path is blocked`() {
        val r = rule.assess(input("/work/project/src/Ghost.kt", FakeProbe(existing = emptySet())))
        assertEquals(RiskAction.BLOCK, r.disposition)
        assertEquals("REFERENCE_EXISTS", r.issues.single().code)
    }

    @Test
    fun `reading an existing path proceeds`() {
        val target = "/work/project/src/A.kt"
        val r = rule.assess(input(target, FakeProbe(existing = setOf(abs(target)))))
        assertEquals(RiskAction.PROCEED, r.disposition)
        assertTrue(r.issues.isEmpty())
    }

    @Test
    fun `a non-existent path outside the workspace is left to other gates`() {
        val r = rule.assess(input("/tmp/elsewhere.txt", FakeProbe(existing = emptySet())))
        assertEquals(RiskAction.PROCEED, r.disposition)
        assertEquals("false", r.observations.single().facts["inWorkspace"])
    }
}
