import com.correx.core.events.types.StageId
import com.correx.core.events.types.TransitionId
import com.correx.core.transitions.graph.StageConfig
import com.correx.core.transitions.graph.TransitionEdge
import com.correx.core.transitions.graph.WorkflowGraph
import com.correx.core.transitions.resolution.TransitionOrdering
import com.correx.core.validation.graph.GraphValidator
import com.correx.core.validation.model.ValidationContext
import com.correx.core.validation.transition.TransitionValidator
import com.correx.testing.fixtures.CycleFixtures
import com.correx.testing.fixtures.WorkflowFixtures
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

class GraphValidatorTest {

    private val validator = GraphValidator()

    @Test
    fun `should detect dangling transition`(): Unit = runBlocking {

        val graph = WorkflowFixtures.simpleGraph().copy(
            stages = mapOf(StageId("A") to StageConfig()) // B removed → dangling
        )

        val result = validator.validate(
            ValidationContext(
                graph = graph,
                detectedCycles = emptyList()
            )
        )

        val issues = result.issues

        assertTrue(
            issues.any { it.code == "GRAPH_DANGLING_TRANSITION" }
        )
    }

    @Test
    fun `should expose cycles as informational only`(): Unit = runBlocking {

        val graph = WorkflowFixtures.simpleGraph()
        val cycles = listOf(CycleFixtures.simpleCycle())

        val validator = GraphValidator()

        val result = validator.validate(
            ValidationContext(
                graph = graph,
                detectedCycles = cycles
            )
        )

        val cycleIssues = result.issues
            .filter { it.code == "GRAPH_CYCLE_DETECTED" }

        assertTrue(cycleIssues.isNotEmpty())
    }

    class TransitionValidatorTest {

        private val validator = TransitionValidator(
            ordering = TransitionOrdering.comparator
        )

        @Test
        fun `should be deterministic`(): Unit = runBlocking {

            val graph = WorkflowFixtures.simpleGraph()

            val result1 = validator.validate(
                ValidationContext(graph, emptyList())
            )

            val result2 = validator.validate(
                ValidationContext(graph, emptyList())
            )

            assertEquals(result1, result2)
        }

        @Test
        fun `should flag non-deterministic order when two edges share same from and to`(): Unit = runBlocking {

            val a = StageId("A")
            val b = StageId("B")

            val edge1 = TransitionEdge(
                id = TransitionId("t1"),
                from = a,
                to = b,
                condition = { true }
            )

            val edge2 = TransitionEdge(
                id = TransitionId("t2"),
                from = a,
                to = b,
                condition = { true }
            )

            val graph = WorkflowGraph(
                id = "workflow-test",
                stages = mapOf(a to StageConfig(), b to StageConfig()),
                transitions = setOf(edge1, edge2),
                start = a
            )

            val result = validator.validate(
                ValidationContext(graph, emptyList())
            )

            assertTrue(
                result.issues.any { it.code == "TRANSITION_NON_DETERMINISTIC_ORDER" },
                "Expected TRANSITION_NON_DETERMINISTIC_ORDER but got: ${result.issues.map { it.code }}"
            )
        }
    }
}
