package com.correx.core.artifacts.kind

import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class KindContractTableTest {

    @AfterTest
    fun reset() {
        KindContractTable.projectOverrides = emptyMap()
    }

    @Test
    fun `infers common TS and Kotlin kinds from path`() {
        assertEquals("react_hook", KindInference.kindFor("frontend/src/hooks/useWebSocket.ts"))
        assertEquals("react_view", KindInference.kindFor("frontend/src/views/Sessions.tsx"))
        assertEquals("react_component", KindInference.kindFor("frontend/src/components/Navbar.tsx"))
        assertEquals("ts_types", KindInference.kindFor("frontend/src/types/index.ts"))
        assertEquals("package_json", KindInference.kindFor("frontend/package.json"))
        assertEquals("tsconfig", KindInference.kindFor("frontend/tsconfig.json"))
        assertEquals("html_entry", KindInference.kindFor("frontend/index.html"))
        assertEquals("kotlin_event", KindInference.kindFor("core/events/FooEvent.kt"))
        assertEquals("kotlin_reducer", KindInference.kindFor("core/foo/BarReducer.kt"))
    }

    @Test
    fun `unknown kind falls back to universal floor, never empty`() {
        val assertions = KindContractTable.assertionsFor(KindInference.kindFor("weird.xyz") ?: "", "weird.xyz")
        assertEquals(listOf("file_exists", "file_nonempty", "parses"), assertions.map { it.id })
    }

    @Test
    fun `app entry files are react_entry, not react_view`() {
        // Regression: main.tsx / index.tsx mount the tree and export nothing. Classifying them as
        // react_view (which requires exports_default_component) made a correct file unwinnable.
        assertEquals("react_entry", KindInference.kindFor("frontend/src/main.tsx"))
        assertEquals("react_entry", KindInference.kindFor("frontend/src/index.tsx"))
        // A non-entry .tsx entry point name in .ts form is unaffected.
        assertEquals("ts_types", KindInference.kindFor("frontend/src/types/index.ts"))
    }

    @Test
    fun `react_entry contract does not require a default component export`() {
        val ids = KindContractTable.assertionsFor("react_entry", "frontend/src/main.tsx").map { it.id }
        assertTrue("file_exists" in ids && "file_nonempty" in ids)
        assertTrue("exports_default_component" !in ids, "entry file must not require a default export")
    }

    @Test
    fun `react_hook contract includes floor plus exports_hook`() {
        val ids = KindContractTable.assertionsFor("react_hook", "useWebSocket.ts").map { it.id }
        assertTrue("file_exists" in ids && "file_nonempty" in ids)
        assertTrue("exports_hook" in ids)
    }

    @Test
    fun `kotlin_event promotes the serialization-registration trap into an assertion`() {
        val ids = KindContractTable.assertionsFor("kotlin_event", "FooEvent.kt").map { it.id }
        assertTrue("registered_in_serialization" in ids)
    }

    @Test
    fun `project override is additive and cannot remove checked-in assertions`() {
        KindContractTable.projectOverrides = mapOf(
            "css_entry" to listOf(
                ContractAssertion("contains", "", AssertionEvaluator.TEXT, args = mapOf("pattern" to "@tailwind")),
                // duplicate of a checked-in id — must not double up
                ContractAssertion("file_exists", "", AssertionEvaluator.FS),
            ),
        )
        val ids = KindContractTable.assertionsFor("css_entry", "src/index.css").map { it.id }
        assertEquals(1, ids.count { it == "file_exists" }, "checked-in file_exists must not be duplicated")
        assertTrue("contains" in ids, "override assertion must be appended")
        assertTrue("file_nonempty" in ids, "checked-in assertions remain")
    }
}
