import com.correx.core.events.events.OrchestrationPausedEvent
import com.correx.core.events.events.OrchestrationResumedEvent
import com.correx.core.events.events.RetryAttemptedEvent
import com.correx.core.events.events.WorkflowCompletedEvent
import com.correx.core.events.events.WorkflowFailedEvent
import com.correx.core.events.events.WorkflowStartedEvent
import com.correx.core.events.orchestration.OrchestrationStatus
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import com.correx.core.kernel.orchestration.DefaultOrchestrationReducer
import com.correx.core.kernel.orchestration.OrchestrationProjector
import com.correx.testing.fixtures.EventFixtures.stored
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

class OrchestrationProjectorTest {

    private val reducer = DefaultOrchestrationReducer()
    private val projector = OrchestrationProjector(reducer)
    private val sessionId = SessionId("s1")
    private val stageId = StageId("stage-1")
    private val workflowId = "workflow-test"

    @Test
    fun `should initialize orchestration with IDLE status`() {
        val initialState = projector.initial()
        assertEquals(OrchestrationStatus.IDLE, initialState.status)
    }

    @Test
    fun `should set RUNNING on WorkflowStartedEvent`() {
        val initialState = projector.initial()
        val started =
            projector.apply(initialState, stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)))
        assertEquals(OrchestrationStatus.RUNNING, started.status)
    }

    @Test
    fun `should set RUNNING after resume on pause`() {
        val initialState = projector.initial()
        val started =
            projector.apply(initialState, stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)))
        assertEquals(OrchestrationStatus.RUNNING, started.status)
        val paused =
            projector.apply(started, stored(payload = OrchestrationPausedEvent(sessionId, stageId, "Tool approval")))
        assertEquals(OrchestrationStatus.PAUSED, paused.status)
        assertTrue(paused.pendingApproval)
        val resumed =
            projector.apply(paused, stored(payload = OrchestrationResumedEvent(sessionId, stageId)))
        assertEquals(OrchestrationStatus.RUNNING, resumed.status)
        assertFalse(resumed.pendingApproval)
    }

    @Test
    fun `should set FAILED on WorkflowFailedEvent`() {
        val initialState = projector.initial()
        val started =
            projector.apply(initialState, stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)))
        val failed =
            projector.apply(
                started,
                stored(payload = WorkflowFailedEvent(sessionId, stageId, "Something went wrong", false)),
            )
        assertEquals(OrchestrationStatus.FAILED, failed.status)
    }

    @Test
    fun `same events produce same result`() {
        val initialState = projector.initial()
        val events = listOf(
            stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)),
            stored(payload = WorkflowFailedEvent(sessionId, stageId, "Something went wrong", false)),
            stored(
                payload = RetryAttemptedEvent(
                    sessionId,
                    stageId,
                    attemptNumber = 1,
                    maxAttempts = 3,
                    failureReason = "Something went wrong",
                ),
            ),
            stored(payload = WorkflowCompletedEvent(sessionId, stageId, 1)),
        )

        val result1 = events.fold(initialState) { state, event ->
            projector.apply(state, event)
        }
        val result2 = events.fold(initialState) { state, event ->
            projector.apply(state, event)
        }

        assertEquals(result1, result2)
    }

    @Test
    fun `full pause-resume lifecycle`() {
        val s0 = projector.initial()
        val s1 = projector.apply(s0, stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)))
        val s2 =
            projector.apply(s1, stored(payload = OrchestrationPausedEvent(sessionId, stageId, "approval required")))
        val s3 = projector.apply(s2, stored(payload = OrchestrationResumedEvent(sessionId, stageId)))
        val s4 = projector.apply(s3, stored(payload = WorkflowCompletedEvent(sessionId, stageId, 1)))

        assertEquals(OrchestrationStatus.RUNNING, s1.status)
        assertEquals(OrchestrationStatus.PAUSED, s2.status)
        assertTrue(s2.pendingApproval)
        assertEquals(OrchestrationStatus.RUNNING, s3.status)
        assertFalse(s3.pendingApproval)
        assertEquals(OrchestrationStatus.COMPLETED, s4.status)
    }
}
