import com.correx.core.events.events.EventMetadata
import com.correx.core.events.events.NewEvent
import com.correx.core.events.events.OrchestrationPausedEvent
import com.correx.core.events.events.OrchestrationResumedEvent
import com.correx.core.events.events.WorkflowStartedEvent
import com.correx.core.events.stores.EventStore
import com.correx.core.events.types.EventId
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import com.correx.core.sessions.DefaultSessionReducer
import com.correx.core.sessions.SessionProjector
import com.correx.core.sessions.projections.replay.DefaultEventReplayer
import com.correx.infrastructure.persistence.InMemoryEventStore
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.Clock
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

class SessionReplayDeterminismTest {

    private fun build(store: EventStore) = DefaultEventReplayer(
        store,
        SessionProjector(DefaultSessionReducer()),
    )

    @Test
    fun `same events produce same state`(): Unit = runBlocking {
        val sessionId = SessionId("s1")

        val store1 = InMemoryEventStore()
        val store2 = InMemoryEventStore()

        val events = mapOf(
            EventMetadata(EventId("start"), sessionId, Clock.System.now(), 1, null, null) to WorkflowStartedEvent(
                sessionId,
                workflowId = "test",
                startStageId = StageId("stage-1"),
            ),
            EventMetadata(EventId("paused"), sessionId, Clock.System.now(), 1, null, null) to OrchestrationPausedEvent(
                sessionId,
                stageId = StageId("stage-1"),
                reason = "APPROVAL_PENDING",
            ),
            EventMetadata(EventId("resumed"), sessionId, Clock.System.now(), 1, null, null) to OrchestrationResumedEvent(
                sessionId,
                stageId = StageId("stage-1"),
            ),
        )

        events.forEach { (meta, payload) ->
            store1.append(NewEvent(meta, payload))
            store2.append(NewEvent(meta, payload))
        }

        val replayer1 = build(store1)
        val replayer2 = build(store2)

        val state1 = replayer1.rebuild(sessionId)
        val state2 = replayer2.rebuild(sessionId)

        assertEquals(state1, state2)
    }
}
