package com.correx.core.events.events

import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
 * A confirmed preemptive redirect (freestyle graph re-routing): the run should jump from
 * [fromStageId] to [toStageId] at the next transition boundary, overriding the resolver's edge.
 *
 * This is the **recorded, deterministic** half of the hybrid design — an LLM proposes a target
 * from a free-text steering note and the operator confirms it (via the approval gate), but only
 * the confirmed [toStageId] is recorded here. Replay reads this target and never re-classifies
 * (invariants #8/#9). [sourceNoteId] links back to the originating steering note when present.
 *
 * Consumed exactly once: the override applies it and records [redirectId] on the resulting
 * [TransitionExecutedEvent], so a replay sees it already applied.
 */
@Serializable
@SerialName("PreemptRedirect")
data class PreemptRedirectEvent(
    val redirectId: String,
    val sessionId: SessionId,
    val fromStageId: StageId,
    val toStageId: StageId,
    val sourceNoteId: String? = null,
    val timestampMs: Long,
) : EventPayload

/**
 * A preemptive redirect that failed the safety guard — the target stage is unknown/terminal or
 * its `needs` artifacts aren't satisfied (decision: invalid jumps are blocked). The run continues
 * on its normally-resolved edge. Recording this also marks [redirectId] consumed, so the blocked
 * redirect is not retried on every subsequent boundary.
 */
@Serializable
@SerialName("PreemptRedirectBlocked")
data class PreemptRedirectBlockedEvent(
    val redirectId: String,
    val sessionId: SessionId,
    val toStageId: StageId,
    val reason: String,
    val timestampMs: Long,
) : EventPayload
