package com.correx.core.kernel.orchestration

import com.correx.core.artifacts.kind.AssertionEvaluator
import com.correx.core.artifacts.kind.ContractAssertion
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.createDirectories
import kotlin.io.path.writeText

class FileSystemContractEvaluatorTest {

    private val evaluator = FileSystemContractEvaluator()

    private fun workspace(build: (Path) -> Unit): Path {
        val root = Files.createTempDirectory("contract-gate")
        build(root)
        return root
    }

    private fun assertion(id: String, target: String, ev: AssertionEvaluator, args: Map<String, String> = emptyMap()) =
        ContractAssertion(id, target, ev, args = args)

    @Test
    fun `empty produced file fails file_nonempty`() = runBlocking {
        val root = workspace { r ->
            r.resolve("src/views").createDirectories()
            r.resolve("src/views/Sessions.tsx").writeText("")
        }
        val v = evaluator.evaluate(root, assertion("file_nonempty", "src/views/Sessions.tsx", AssertionEvaluator.FS))
        assertFalse(v.passed, "empty Sessions.tsx must fail file_nonempty")
    }

    @Test
    fun `missing file fails file_exists`() = runBlocking {
        val root = workspace { }
        val v = evaluator.evaluate(root, assertion("file_exists", "src/App.tsx", AssertionEvaluator.FS))
        assertFalse(v.passed, "missing App.tsx must fail file_exists")
    }

    @Test
    fun `hook file without a use-export fails exports_hook`() = runBlocking {
        val root = workspace { r ->
            r.resolve("src/hooks").createDirectories()
            r.resolve("src/hooks/useWebSocket.ts").writeText("export const connect = () => {}")
        }
        val v = evaluator.evaluate(root, assertion("exports_hook", "src/hooks/useWebSocket.ts", AssertionEvaluator.AST))
        assertFalse(v.passed)
    }

    @Test
    fun `real hook export passes`() = runBlocking {
        val root = workspace { r ->
            r.resolve("src/hooks").createDirectories()
            r.resolve("src/hooks/useWebSocket.ts").writeText("export const useWebSocket = () => {}")
        }
        val v = evaluator.evaluate(root, assertion("exports_hook", "src/hooks/useWebSocket.ts", AssertionEvaluator.AST))
        assertTrue(v.passed)
    }

    @Test
    fun `package_json missing build script fails script_defined`() = runBlocking {
        val root = workspace { r ->
            r.resolve("package.json").writeText("""{"name":"x","scripts":{"dev":"vite"}}""")
        }
        val v = evaluator.evaluate(
            root,
            assertion("script_defined", "package.json", AssertionEvaluator.TEXT, mapOf("name" to "build")),
        )
        assertFalse(v.passed)
    }

    @Test
    fun `tsconfig with comments and trailing commas passes valid_json (JSONC)`() = runBlocking {
        // Regression: tsconfig.json is JSONC — tsc/Vite accept comments + trailing commas. A strict
        // valid_json false-failed a legitimate config and wasted a retry.
        val root = workspace { r ->
            r.resolve("tsconfig.json").writeText(
                """
                {
                  // compiler options for the app
                  "compilerOptions": {
                    "strict": true,
                    "jsx": "react-jsx",
                  },
                }
                """.trimIndent(),
            )
        }
        val v = evaluator.evaluate(root, assertion("valid_json", "tsconfig.json", AssertionEvaluator.TEXT))
        assertTrue(v.passed, "tsconfig JSONC must pass valid_json")
    }

    @Test
    fun `compiler assertions are skipped, not failed`() = runBlocking {
        val root = workspace { }
        val v = evaluator.evaluate(root, assertion("parses", "whatever.ts", AssertionEvaluator.COMPILER))
        assertTrue(v.skipped)
        assertTrue(v.passed, "a skipped assertion must not fail the stage")
    }
}
