package dev.kaizen.app

import dev.kaizen.app.net.StreamConsumer
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test

class StreamConsumerTest {

    private val usage = 0.toChar()
    private val reasoning = 1.toChar()
    private val sources = 2.toChar()
    private val tool = 3.toChar()
    private val query = 4.toChar()

    // ── Batch parser (visibleText) ──────────────────────────────────────────

    @Test
    fun plainText_passesThrough() {
        assertEquals("Hallo Welt", StreamConsumer.visibleText("Hallo Welt"))
    }

    @Test
    fun usageSentinel_isClipped() {
        assertEquals("Antwort.", StreamConsumer.visibleText("Antwort.$usage{\"i\":12,\"o\":34}"))
    }

    @Test
    fun reasoning_isStripped_contentKept() {
        assertEquals("Die Antwort ist 42.", StreamConsumer.visibleText("${reasoning}denke nach${reasoning}Die Antwort ist 42."))
    }

    @Test
    fun reasoning_thenContent_thenUsage() {
        assertEquals("Fertig.", StreamConsumer.visibleText("${reasoning}grübel${reasoning}Fertig.$usage{\"i\":1,\"o\":2}"))
    }

    @Test
    fun searchQueryAndSources_areClipped() {
        assertEquals("Hier das Wetter.", StreamConsumer.visibleText("Hier das Wetter.${query}\"wetter\"${sources}[{\"url\":\"x\"}]$usage{\"i\":1,\"o\":2}"))
    }

    @Test
    fun leadingToolEvents_areStripped() {
        assertEquals("Die Antwort.", StreamConsumer.visibleText("$tool{\"type\":\"thinking\"}\n${tool}{\"type\":\"start\",\"name\":\"x\"}\nDie Antwort."))
    }

    @Test
    fun emptyBuffer_returnsEmpty() {
        assertEquals("", StreamConsumer.visibleText(""))
    }

    // ── Incremental parser — content ───────────────────────────────────────

    @Test
    fun incremental_plainText() {
        val inc = StreamConsumer.Incremental()
        assertEquals("Hallo Welt", inc.append("Hallo Welt").content)
    }

    @Test
    fun incremental_plainText_charByChar() {
        val inc = StreamConsumer.Incremental()
        var state = inc.append("")
        for (c in "Hallo Welt") state = inc.append(c.toString())
        assertEquals("Hallo Welt", state.content)
    }

    @Test
    fun incremental_usageSentinel_isClipped() {
        val inc = StreamConsumer.Incremental()
        inc.append("Antwort.")
        assertEquals("Antwort.", inc.append("$usage{\"i\":12,\"o\":34}").content)
    }

    @Test
    fun incremental_reasoning_captured() {
        val inc = StreamConsumer.Incremental()
        inc.append("${reasoning}denke nach")
        val state = inc.append("${reasoning}Die Antwort.")
        assertEquals("Die Antwort.", state.content)
        assertEquals("denke nach", state.reasoning)
    }

    @Test
    fun incremental_reasoning_thenContent_thenUsage() {
        val inc = StreamConsumer.Incremental()
        inc.append("${reasoning}grübel${reasoning}")
        val state = inc.append("Fertig.$usage{\"i\":1,\"o\":2}")
        assertEquals("Fertig.", state.content)
        assertEquals("grübel", state.reasoning)
    }

    @Test
    fun incremental_sources_parsed() {
        val inc = StreamConsumer.Incremental()
        val state = inc.append("Wetter.${query}\"berlin wetter\"${sources}[{\"title\":\"t\",\"url\":\"u\",\"snippet\":\"s\"}]$usage{}")
        assertEquals("Wetter.", state.content)
        assertEquals("\"berlin wetter\"", state.query)
        assertEquals(1, state.sources.size)
        assertEquals("t", state.sources[0].title)
        assertEquals("u", state.sources[0].url)
    }

    @Test
    fun incremental_toolEvents_parsed() {
        val inc = StreamConsumer.Incremental()
        val state = inc.append("$tool{\"type\":\"start\",\"name\":\"web_search\"}\n${tool}{\"type\":\"end\",\"name\":\"web_search\",\"elapsed\":120}\nAntwort.")
        assertEquals("Antwort.", state.content)
        assertEquals(2, state.tools.size)
        assertEquals("start", state.tools[0].type)
        assertEquals("web_search", state.tools[0].name)
        assertEquals("end", state.tools[1].type)
        assertEquals(120L, state.tools[1].elapsed)
    }

    @Test
    fun incremental_toolEvents_splitAcrossChunks() {
        val inc = StreamConsumer.Incremental()
        inc.append("$tool{\"type\":\"sta")
        inc.append("rt\",\"name\":\"x\"}\n")
        val state = inc.append("Content.")
        assertEquals("Content.", state.content)
        assertEquals(1, state.tools.size)
    }

    @Test
    fun incremental_afterDone_ignoresMore() {
        val inc = StreamConsumer.Incremental()
        inc.append("Fertig.")
        inc.append("${usage}{\"i\":1}")
        assertEquals("Fertig.", inc.append("ignored").content)
    }

    @Test
    fun incremental_fullStream_allSections() {
        val full = "$tool{\"type\":\"start\",\"name\":\"t\"}\n${reasoning}grübel${reasoning}Die Antwort.${query}\"q\"${sources}[{\"title\":\"x\",\"url\":\"y\",\"snippet\":\"z\"}]$usage{}"
        val inc = StreamConsumer.Incremental()
        var state = inc.append("")
        for (c in full) state = inc.append(c.toString())
        assertEquals("Die Antwort.", state.content)
        assertEquals("grübel", state.reasoning)
        assertEquals(1, state.tools.size)
        assertEquals("t", state.tools[0].name)
        assertEquals("\"q\"", state.query)
        assertEquals(1, state.sources.size)
        assertEquals("x", state.sources[0].title)
    }
}
