import com.correx.core.approvals.ApprovalProjector
import com.correx.core.approvals.DefaultApprovalReducer
import com.correx.core.approvals.DefaultApprovalRepository
import com.correx.core.approvals.domain.DefaultApprovalEngine
import com.correx.core.artifacts.DefaultArtifactReducer
import com.correx.testing.fixtures.context.ContextFixtures
import com.correx.core.events.events.TransitionExecutedEvent
import com.correx.core.events.events.WorkflowCompletedEvent
import com.correx.core.events.execution.RetryPolicy
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import com.correx.core.events.types.TransitionId
import com.correx.core.inference.InferenceRepository
import com.correx.core.inference.InferenceState
import com.correx.core.journal.DecisionJournalProjector
import com.correx.core.journal.DefaultDecisionJournalReducer
import com.correx.core.journal.DefaultDecisionJournalRepository
import com.correx.core.kernel.orchestration.DefaultOrchestrationReducer
import com.correx.core.kernel.orchestration.DefaultSessionOrchestrator
import com.correx.core.kernel.orchestration.OrchestrationConfig
import com.correx.core.kernel.orchestration.OrchestrationProjector
import com.correx.core.kernel.orchestration.OrchestrationRepository
import com.correx.core.kernel.orchestration.OrchestratorEngines
import com.correx.core.kernel.orchestration.OrchestratorRepositories
import com.correx.core.kernel.retry.DefaultRetryCoordinator
import com.correx.core.risk.DefaultRiskAssessor
import com.correx.core.sessions.DefaultSessionRepository
import com.correx.core.sessions.projections.replay.DefaultEventReplayer
import com.correx.core.sessions.projections.replay.EventReplayer
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.validation.pipeline.ValidationPipeline
import com.correx.infrastructure.persistence.InMemoryEventStore
import com.correx.infrastructure.persistence.artifact.LiveArtifactRepository
import com.correx.testing.contracts.fixtures.artifactstore.NoopArtifactStore
import com.correx.testing.fixtures.InferenceFixtures
import com.correx.testing.fixtures.cyclePolicyMissingValidator
import com.correx.testing.fixtures.transitions.TransitionFixtures
import com.correx.testing.kernel.MockSessionEventReplayer
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test

/**
 * Regression guard for the [com.correx.core.kernel.orchestration.subagent.SubagentRunner] seam:
 * stage execution is now dispatched through the runner rather than calling executeStage inline.
 * The default in-session runner must produce behaviour identical to the prior path — a workflow
 * still runs every non-terminal stage to completion. (executeStage is module-internal, so the
 * runner cannot be wrapped from this test module; we assert per-stage execution end-to-end.)
 */
class SubagentRunnerSeamTest {

    private val eventStore = InMemoryEventStore()
    private val sessionReplayer = MockSessionEventReplayer()
    private val sessionRepository = DefaultSessionRepository(sessionReplayer)
    private val orchestrationRepository = OrchestrationRepository(
        DefaultEventReplayer(eventStore, OrchestrationProjector(DefaultOrchestrationReducer())),
    )
    private val inferenceRepository = InferenceRepository(
        object : EventReplayer<InferenceState> {
            override fun rebuild(sessionId: SessionId) = InferenceState()
        },
    )
    private val approvalRepository = DefaultApprovalRepository(
        DefaultEventReplayer(eventStore, ApprovalProjector(DefaultApprovalReducer())),
    )
    private val decisionJournalRepository = DefaultDecisionJournalRepository(
        DefaultEventReplayer(eventStore, DecisionJournalProjector(DefaultDecisionJournalReducer())),
    )
    private val artifactStore = NoopArtifactStore()
    private val retryCoordinator = DefaultRetryCoordinator(eventStore)

    private val repositories = OrchestratorRepositories(
        eventStore = eventStore,
        inferenceRepository = inferenceRepository,
        orchestrationRepository = orchestrationRepository,
        sessionRepository = sessionRepository,
        artifactRepository = LiveArtifactRepository(eventStore, DefaultArtifactReducer()),
        approvalRepository = approvalRepository,
    )

    private val orchestrator = DefaultSessionOrchestrator(
        repositories = repositories,
        engines = OrchestratorEngines(
            transitionResolver = TransitionFixtures.simpleResolver(),
            contextPackBuilder = ContextFixtures.simpleBuilder(),
            inferenceRouter = InferenceFixtures.fixedRouter(),
            validationPipeline = ValidationPipeline(validators = listOf(cyclePolicyMissingValidator())),
            approvalEngine = DefaultApprovalEngine(),
            riskAssessor = DefaultRiskAssessor(),
        ),
        retryCoordinator = retryCoordinator,
        artifactStore = artifactStore,
        decisionJournalRepository = decisionJournalRepository,
    )

    private val defaultConfig = OrchestrationConfig(
        retryPolicy = RetryPolicy(maxAttempts = 1, backoffMs = 0),
    )

    @Test
    fun `workflow runs every non-terminal stage through the runner and completes`(): Unit = runBlocking {
        val sessionId = SessionId("seam-test-1")
        val stageA = StageId("A")
        val stageB = StageId("B")
        val done = StageId("done")

        val graph = WorkflowGraph(
            id = "seam-test",
            stages = mapOf(
                stageA to StageConfig(allowedTools = emptySet()),
                stageB to StageConfig(allowedTools = emptySet()),
                done to StageConfig(),
            ),
            transitions = setOf(
                TransitionEdge(id = TransitionId("t1"), from = stageA, to = stageB, condition = { true }),
                TransitionEdge(id = TransitionId("t2"), from = stageB, to = done, condition = { true }),
            ),
            start = stageA,
        )

        orchestrator.run(sessionId, graph, defaultConfig)

        val events = eventStore.read(sessionId)
        assertNotNull(
            events.find { it.payload is WorkflowCompletedEvent },
            "workflow should complete through the subagent runner seam",
        )
        // Stage A is the start (entered, no transition-to event); B is entered via the runner
        // after the A->B transition; `done` is a terminal sentinel (no TransitionExecuted).
        val transitions = events.mapNotNull { it.payload as? TransitionExecutedEvent }
        assertEquals(
            listOf(stageB),
            transitions.map { it.to },
            "stage B should be entered via the runner after stage A completes",
        )
    }
}
