package com.correx.apps.server.bridge

import com.correx.apps.server.protocol.RiskSummaryDto
import com.correx.apps.server.protocol.ServerMessage
import com.correx.apps.server.protocol.toDto
import com.correx.core.artifactstore.ArtifactStore
import com.correx.core.events.events.ApprovalDecisionResolvedEvent
import com.correx.core.events.events.ApprovalRequestedEvent
import com.correx.core.events.events.ArtifactContentStoredEvent
import com.correx.core.events.events.ArtifactCreatedEvent
import com.correx.core.events.events.ArtifactValidatedEvent
import com.correx.core.events.events.ArtifactValidatingEvent
import com.correx.core.events.events.ClarificationRequestedEvent
import com.correx.core.events.events.ExecutionPlanLockedEvent
import com.correx.core.events.events.ModelLoadedEvent
import com.correx.core.events.events.ModelUnloadedEvent
import com.correx.core.events.events.PreemptRedirectBlockedEvent
import com.correx.core.events.events.PreemptRedirectEvent
import com.correx.core.events.events.StoredEvent
import com.correx.core.events.events.TalkieNarrationEvent
import com.correx.core.events.events.WorkflowProposedEvent
import com.correx.core.events.risk.RiskAction

/**
 * Approval / clarification / workflow-proposal / narration / artifact / model / preempt events —
 * the remaining operator-facing (or deliberately suppressed) lifecycle surface. Branches that
 * return `null` are handled-but-not-surfaced (transient bookkeeping); wrapping them in
 * [MapOutcome.Emit] keeps them from falling through to the dispatcher's "unmapped" log.
 */
@Suppress("UnusedParameter", "LongMethod", "CyclomaticComplexMethod")
internal suspend fun mapLifecycleEvent(
    event: StoredEvent,
    artifactStore: ArtifactStore,
    sessionSequence: Long,
): MapOutcome {
    val seq = event.sequence
    val msg: ServerMessage? = when (val p = event.payload) {
        is ApprovalRequestedEvent -> mapApprovalRequested(p, seq, sessionSequence)
        is ClarificationRequestedEvent -> ServerMessage.ClarificationRequired(
            sessionId = p.sessionId,
            requestId = p.requestId,
            stageId = p.stageId,
            questions = p.questions,
            sequence = seq,
            sessionSequence = sessionSequence,
        )

        is WorkflowProposedEvent -> ServerMessage.WorkflowProposed(
            sessionId = p.sessionId,
            proposalId = p.proposalId,
            prompt = p.prompt,
            candidates = p.candidates,
            originalRequest = p.originalRequest,
            sequence = seq,
            sessionSequence = sessionSequence,
        )

        is ApprovalDecisionResolvedEvent -> ServerMessage.ApprovalResolved(
            // ApprovalDecisionResolvedEvent has no sessionId on its payload — read it from the event envelope
            sessionId = event.metadata.sessionId,
            requestId = p.requestId,
            outcome = p.outcome.name,
            reason = p.reason,
            sequence = seq,
            sessionSequence = sessionSequence,
        )

        is TalkieNarrationEvent -> ServerMessage.Narration(
            sessionId = p.sessionId,
            content = p.content,
            stageId = p.stageId,
            latencyMs = p.latencyMs,
            totalTokens = p.tokensUsed?.totalTokens,
            sequence = seq,
            sessionSequence = sessionSequence,
        )

        is ArtifactCreatedEvent -> ServerMessage.ArtifactCreated(
            sessionId = p.sessionId,
            stageId = p.stageId,
            artifactId = p.artifactId,
            sequence = seq,
            sessionSequence = sessionSequence,
        )

        is ArtifactValidatedEvent -> ServerMessage.ArtifactValidated(
            sessionId = p.sessionId,
            stageId = p.stageId,
            artifactId = p.artifactId,
            sequence = seq,
            sessionSequence = sessionSequence,
        )

        // Transient pre-validation marker, emitted microseconds before Validated or a
        // stage failure — either of those carries the outcome the operator cares about.
        is ArtifactValidatingEvent -> null

        is ExecutionPlanLockedEvent -> ServerMessage.PlanLocked(
            sessionId = p.sessionId,
            workflowId = p.workflowId,
            stageIds = p.stageIds,
            sequence = seq,
            sessionSequence = sessionSequence,
        )

        is ModelLoadedEvent -> ServerMessage.ModelChanged(
            modelId = p.modelId,
            providerId = p.providerId.value,
            loaded = true,
        )

        is ModelUnloadedEvent -> ServerMessage.ModelChanged(
            modelId = p.modelId,
            providerId = p.providerId.value,
            loaded = false,
        )

        // Internal slot→CAS-hash bookkeeping (F-007 durable bridge); no operator-facing surface.
        is ArtifactContentStoredEvent -> null

        // Freestyle graph-rerouting bookkeeping. The deterministic record is in place; a dedicated
        // operator surface ships with the LLM-proposal + approval-confirm front-half.
        is PreemptRedirectEvent -> null
        is PreemptRedirectBlockedEvent -> null

        else -> return MapOutcome.Skip
    }
    return MapOutcome.Emit(msg)
}

private fun mapApprovalRequested(
    p: ApprovalRequestedEvent,
    seq: Long,
    sessionSequence: Long,
): ServerMessage =
    ServerMessage.ApprovalRequired(
        sessionId = p.sessionId,
        requestId = p.requestId,
        tier = p.tier,
        riskSummary = p.riskSummary?.toDto() ?: RiskSummaryDto(
            level = p.tier.name,
            factors = emptyList(),
            recommendedAction = RiskAction.PROMPT_USER.name,
            rationale = emptyList(),
        ),
        toolName = p.toolName,
        preview = p.preview,
        sequence = seq,
        sessionSequence = sessionSequence,
    )
