package com.correx.infrastructure.compression

import com.correx.core.context.compression.TokenPruner
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.engine.cio.CIO
import io.ktor.http.ContentType
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.withTimeout
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.slf4j.LoggerFactory

/**
 * [TokenPruner] backed by the LLMLingua-2 sidecar (sidecars/llmlingua/server.py) over localhost
 * HTTP. Protected spans are forwarded so the classifier keeps them verbatim. Fails *open*: any
 * error (sidecar down, timeout, malformed) returns the original content — compression is an
 * optimization, never a correctness dependency, so a missing sidecar must not break inference.
 */
class HttpTokenPruner(
    private val baseUrl: String = "http://127.0.0.1:8199",
    private val timeoutMs: Long = DEFAULT_TIMEOUT_MS,
    private val client: HttpClient = defaultClient(),
) : TokenPruner {

    private val log = LoggerFactory.getLogger(HttpTokenPruner::class.java)

    override suspend fun prune(content: String, protectedSpans: List<String>, targetRatio: Double): String =
        runCatching {
            withTimeout(timeoutMs) {
                val resp: PruneResponse = client.post("$baseUrl/prune") {
                    contentType(ContentType.Application.Json)
                    setBody(PruneRequest(content, protectedSpans, targetRatio))
                }.body()
                resp.compressed
            }
        }.getOrElse { e ->
            log.debug("token pruning sidecar unavailable ({}), passing content through", e.message)
            content
        }

    @Serializable
    private data class PruneRequest(val text: String, val protected: List<String>, val rate: Double)

    @Serializable
    private data class PruneResponse(val compressed: String)

    private companion object {
        const val DEFAULT_TIMEOUT_MS = 5000L
        fun defaultClient() = HttpClient(CIO) {
            install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
        }
    }
}
