package com.correx.core.kernel.orchestration

import com.correx.core.context.model.ContextEntry
import com.correx.core.context.model.ContextLayer
import com.correx.core.context.model.EntryRole
import com.correx.core.events.events.FileWrittenEvent
import com.correx.core.events.events.StoredEvent
import com.correx.core.events.events.WorkspaceVerificationObservedEvent
import com.correx.core.events.types.ContextEntryId
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import com.correx.core.transitions.graph.BuildExpectation
import java.util.UUID

/**
 * Replay-safe workspace state key (design 2026-07-15 seam 2). The workspace's verified identity is
 * the folded FileWritten manifest — each path mapped to its latest recorded post-image hash. Hashing
 * the sorted map yields a key that changes iff a file changed, so a verification observation stamped
 * with this key is invalidated automatically by any later write. Derived purely from recorded events
 * (never rescans the filesystem), so replay reproduces the same key (invariants #8/#9).
 */
internal fun workspaceStateKey(events: List<StoredEvent>): String {
    val latest = linkedMapOf<String, String>()
    events.mapNotNull { it.payload as? FileWrittenEvent }
        .forEach { fw -> latest[fw.path] = fw.postImageHash ?: "" }
    val canonical = latest.entries.sortedBy { it.key }.joinToString("\n") { "${it.key}=${it.value}" }
    return Integer.toHexString(canonical.hashCode())
}

/**
 * Record the known-good verification observation for a passing build gate, binding the result to the
 * current [workspaceStateKey]. Called from the execution gate on a clean run so the next stage can
 * tell a still-valid baseline from a stale one. Emits nothing for [BuildExpectation.NONE] (no build
 * claim manufactured — spec: a docs-only/no-write stage does not fabricate a compiler assertion).
 */
internal suspend fun SessionOrchestrator.recordWorkspaceVerification(
    sessionId: SessionId,
    stageId: StageId,
    expectation: BuildExpectation,
    command: String,
    passed: Boolean,
) {
    val events = eventStore.read(sessionId)
    emit(
        sessionId,
        WorkspaceVerificationObservedEvent(
            sessionId = sessionId,
            stageId = stageId,
            stateKey = workspaceStateKey(events),
            expectation = expectation.name,
            command = command,
            passed = passed,
            changedPaths = stageWrittenPaths(sessionId, stageId),
        ),
    )
}

/**
 * The `verified baseline` L0 entry for the next implementation stage (design 2026-07-15 seam 2). If
 * the latest passing [WorkspaceVerificationObservedEvent]'s state key still equals the CURRENT state
 * key, the workspace is known-good and the entry states the verified command + changed paths. If the
 * workspace changed since (key mismatch), the entry says the baseline is stale and the stage must
 * re-verify — it never claims a skipped compiler assertion passed. Empty when nothing has verified yet.
 */
internal suspend fun SessionOrchestrator.verifiedBaselineEntries(sessionId: SessionId): List<ContextEntry> {
    val events = eventStore.read(sessionId)
    val lastPass = events.mapNotNull { it.payload as? WorkspaceVerificationObservedEvent }
        .lastOrNull { it.passed } ?: return emptyList()
    val current = workspaceStateKey(events)
    val fresh = current == lastPass.stateKey
    val content = if (fresh) {
        "## Verified baseline (known-good)\nThe workspace passed `${lastPass.command}` " +
            "(${lastPass.expectation}) at the current state. Build/typecheck is green as of these paths: " +
            "${lastPass.changedPaths.joinToString(", ").ifBlank { "(no declared write paths)" }}. " +
            "Build on it; do not re-derive what already compiles."
    } else {
        "## Verified baseline (STALE)\nThe last green verification (`${lastPass.command}`) was for an " +
            "earlier workspace state; files changed since, so it is no longer authoritative. Re-run the " +
            "build gate before treating the workspace as known-good."
    }
    return listOf(
        ContextEntry(
            id = ContextEntryId(UUID.randomUUID().toString()),
            layer = ContextLayer.L0,
            content = content,
            sourceType = "verifiedBaseline",
            sourceId = lastPass.stateKey,
            tokenEstimate = estimateTokens(content),
            // #312: flips known-good → STALE the moment a write lands, i.e. it changes within a
            // single stage. Mutable ⇒ USER (stays L0, so it is still pinned and never pruned).
            role = EntryRole.USER,
        ),
    )
}
