package com.correx.apps.server.bridge

import com.correx.apps.server.protocol.ServerMessage
import com.correx.core.artifactstore.ArtifactStore
import com.correx.core.events.events.StoredEvent
import com.correx.core.events.types.ArtifactId
import org.slf4j.LoggerFactory

internal val log = LoggerFactory.getLogger("DomainEventMapper")

class DomainEventMapper(private val artifactStore: ArtifactStore = NoopArtifactStore) {
    suspend fun map(event: StoredEvent, sessionSequence: Long = 0L): ServerMessage? =
        domainEventToServerMessage(event, artifactStore, sessionSequence = sessionSequence)
}

private object NoopArtifactStore : ArtifactStore {
    override suspend fun put(bytes: ByteArray): ArtifactId = ArtifactId("noop")
    override suspend fun get(id: ArtifactId): ByteArray? = null
    override suspend fun flushBefore(commit: suspend () -> Unit) = commit()
}

/**
 * Outcome of one per-domain mapper. [Emit] means "this event is mine" — carrying either a
 * [ServerMessage] or `null` (handled but deliberately not surfaced, e.g. transient bookkeeping
 * events). [Skip] means "not my domain", so the dispatcher tries the next mapper. The distinction
 * matters: chaining on a bare `null` would conflate suppression with non-ownership.
 */
internal sealed interface MapOutcome {
    @JvmInline
    value class Emit(val message: ServerMessage?) : MapOutcome
    data object Skip : MapOutcome
}

// Per-domain mappers, tried in order. Each owns a disjoint slice of the payload hierarchy and
// returns [MapOutcome.Skip] for anything outside it. Split across files by domain area to keep
// each mapper and its import list small — see StageInferenceEventMappers, ToolEventMappers, etc.
private val domainMappers: List<suspend (StoredEvent, ArtifactStore, Long) -> MapOutcome> = listOf(
    ::mapSessionEvent,
    ::mapStageInferenceEvent,
    ::mapToolEvent,
    ::mapLifecycleEvent,
)

suspend fun domainEventToServerMessage(
    event: StoredEvent,
    artifactStore: ArtifactStore,
    sessionSequence: Long = 0L,
): ServerMessage? {
    for (mapper in domainMappers) {
        when (val outcome = mapper(event, artifactStore, sessionSequence)) {
            is MapOutcome.Emit -> return outcome.message
            MapOutcome.Skip -> Unit
        }
    }
    log.debug(
        "DomainEventMapper: unmapped payload type={} sessionId={} sequence={}",
        event.payload::class.simpleName,
        event.metadata.sessionId,
        event.sequence,
    )
    return null
}
