package com.correx.core.journal

import com.correx.core.journal.model.DecisionJournalState
import com.correx.core.journal.model.DecisionKind
import com.correx.core.journal.model.DecisionRecord

class DecisionJournalRenderer(private val keepLast: Int = 40) {
    fun render(state: DecisionJournalState, summaryText: String? = null): String {
        if (state.records.isEmpty() && summaryText == null) return ""
        val visible = foldResolvedRetries(state.records).filter { it.kind in AGENT_RELEVANT_KINDS }
        return buildString {
            appendLine("## Decision history (chronological — ground truth)")
            if (summaryText != null) {
                appendLine(summaryText)
                if (state.lowSalienceOmittedCount > 0) {
                    appendLine("+${state.lowSalienceOmittedCount} routine transitions/retries omitted")
                }
            }
            visible.takeLast(keepLast).forEach { appendLine("- ${it.summary}") }
        }.trimEnd()
    }

    /**
     * A RETRY record is resolved once a later TRANSITION leaves its stage — the failure was
     * overcome and its details are noise for every subsequent stage. Fold each resolved stage's
     * retries into one summary line at the position of the first retry; unresolved (still-live)
     * retries render verbatim. Render-time only: the journal state and event log are untouched.
     */
    private fun foldResolvedRetries(records: List<DecisionRecord>): List<DecisionRecord> {
        val resolvedAt = records
            .filter { it.kind == DecisionKind.TRANSITION && it.fromStageId != null }
            .groupBy { it.fromStageId!! }
            .mapValues { (_, transitions) -> transitions.maxOf { it.sequence } }
        val foldedStages = mutableSetOf<String>()
        return records.mapNotNull { record ->
            val stage = record.stageId
            val resolution = stage?.let { resolvedAt[it] }
            if (record.kind != DecisionKind.RETRY || resolution == null || record.sequence > resolution) {
                return@mapNotNull record
            }
            if (stage in foldedStages) return@mapNotNull null
            foldedStages += stage
            val count = records.count {
                it.kind == DecisionKind.RETRY && it.stageId == stage && it.sequence <= resolution
            }
            record.copy(summary = "$stage: $count retr${if (count == 1) "y" else "ies"}, resolved — stage completed")
        }
    }

    companion object {
        /**
         * What actually shapes an agent's next decision: the goal, human steering, and failures
         * worth not repeating. APPROVAL/ARTIFACT/TRANSITION are bookkeeping for the audit trail —
         * agents don't act on "Approval APPROVED (tier T2)" or "Advanced x → y", so they're
         * dropped from the render (still present in [DecisionJournalState.records] for the journal).
         */
        private val AGENT_RELEVANT_KINDS = setOf(
            DecisionKind.INTENT,
            DecisionKind.STEERING,
            DecisionKind.PREEMPT,
            DecisionKind.FAILURE,
            DecisionKind.RETRY,
        )
    }
}
