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
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

class DecisionJournalRendererTest {

    private val renderer = DecisionJournalRenderer()

    private fun record(seq: Long, kind: DecisionKind = DecisionKind.INTENT, summary: String = "summary-$seq") =
        DecisionRecord(sequence = seq, kind = kind, summary = summary)

    @Test
    fun `empty state with no summaryText returns empty string`() {
        val state = DecisionJournalState()
        assertEquals("", renderer.render(state))
    }

    @Test
    fun `uncompacted state renders records without omitted line`() {
        val state = DecisionJournalState(
            records = listOf(record(1), record(2)),
        )
        val result = renderer.render(state)
        assertTrue(result.contains("- summary-1"))
        assertTrue(result.contains("- summary-2"))
        assertFalse(result.contains("omitted"))
    }

    @Test
    fun `approval, artifact and transition records are excluded from the render`() {
        val state = DecisionJournalState(
            records = listOf(
                record(1, DecisionKind.APPROVAL, "Approval APPROVED (tier T2)"),
                record(2, DecisionKind.ARTIFACT, "Validated artifact x at stage y"),
                record(3, DecisionKind.TRANSITION, "Advanced a → b (t1)").copy(stageId = "b", fromStageId = "a"),
                record(4, DecisionKind.FAILURE, "Stage b failed: boom"),
            ),
        )
        val result = renderer.render(state)
        assertFalse(result.contains("Approval APPROVED"))
        assertFalse(result.contains("Validated artifact"))
        assertFalse(result.contains("Advanced a"))
        assertTrue(result.contains("- Stage b failed: boom"))
    }

    @Test
    fun `summaryText appears before records`() {
        val state = DecisionJournalState(
            records = listOf(record(1)),
            lowSalienceOmittedCount = 3,
        )
        val result = renderer.render(state, summaryText = "Compaction summary here")
        val lines = result.lines()
        val headerIdx = lines.indexOfFirst { it.startsWith("## Decision history") }
        val summaryIdx = lines.indexOfFirst { it == "Compaction summary here" }
        val recordIdx = lines.indexOfFirst { it.startsWith("- summary") }
        assertTrue(headerIdx >= 0)
        assertTrue(summaryIdx > headerIdx)
        assertTrue(recordIdx > summaryIdx)
    }

    @Test
    fun `count line appears when lowSalienceOmittedCount is positive`() {
        val state = DecisionJournalState(
            records = listOf(record(1)),
            lowSalienceOmittedCount = 5,
        )
        val result = renderer.render(state, summaryText = "Some summary")
        assertTrue(result.contains("+5 routine transitions/retries omitted"))
    }

    @Test
    fun `no count line when lowSalienceOmittedCount is zero with summaryText`() {
        val state = DecisionJournalState(
            records = listOf(record(1)),
            lowSalienceOmittedCount = 0,
        )
        val result = renderer.render(state, summaryText = "Some summary")
        assertFalse(result.contains("omitted"))
    }

    @Test
    fun `summaryText with empty records list renders header and summary`() {
        val state = DecisionJournalState(records = emptyList(), lowSalienceOmittedCount = 2)
        val result = renderer.render(state, summaryText = "Compacted summary")
        assertTrue(result.contains("## Decision history"))
        assertTrue(result.contains("Compacted summary"))
        assertTrue(result.contains("+2 routine transitions/retries omitted"))
        assertFalse(result.contains("- "))
    }

    @Test
    fun `resolved retries fold to a single summary line`() {
        val state = DecisionJournalState(
            records = listOf(
                record(1, DecisionKind.RETRY, "Retry 1/3 of scaffold: did not produce").copy(stageId = "scaffold"),
                record(2, DecisionKind.RETRY, "Retry 2/3 of scaffold: did not produce").copy(stageId = "scaffold"),
                record(3, DecisionKind.TRANSITION, "Advanced scaffold → hook (t1)")
                    .copy(stageId = "hook", fromStageId = "scaffold"),
            ),
        )
        val result = renderer.render(state)
        assertFalse(result.contains("did not produce"), "resolved retry detail must not render")
        assertTrue(result.contains("- scaffold: 2 retries, resolved — stage completed"))
        assertFalse(result.contains("Advanced scaffold"), "TRANSITION records are audit-only, not agent-relevant")
    }

    @Test
    fun `unresolved retries render verbatim`() {
        val state = DecisionJournalState(
            records = listOf(
                record(1, DecisionKind.RETRY, "Retry 1/3 of hook: did not produce").copy(stageId = "hook"),
            ),
        )
        val result = renderer.render(state)
        assertTrue(result.contains("- Retry 1/3 of hook: did not produce"))
    }

    @Test
    fun `retries after the stage was last left are not folded`() {
        val state = DecisionJournalState(
            records = listOf(
                record(1, DecisionKind.TRANSITION, "Advanced scaffold → hook (t1)")
                    .copy(stageId = "hook", fromStageId = "scaffold"),
                record(2, DecisionKind.RETRY, "Retry 1/3 of scaffold: regression").copy(stageId = "scaffold"),
            ),
        )
        val result = renderer.render(state)
        assertTrue(result.contains("- Retry 1/3 of scaffold: regression"))
    }
}
