package com.correx.core.talkie

import com.correx.core.events.events.ProposedWorkflow
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive

/**
 * Parses an optional `propose_workflows` block out of a router chat reply: the triage path may end
 * its prose with a fenced ```` ```json {"propose_workflows":[{"id":..,"reason":..}],"prompt":..} ``` ````
 * block. Lenient and fence-tolerant — any parse failure or an absent/empty array yields no proposal.
 * Returns the reply with the block stripped (so the operator sees only prose) alongside the parsed
 * candidates, so the router records a structured [com.correx.core.events.events.WorkflowProposedEvent]
 * while still showing a clean chat turn.
 */
object WorkflowProposals {

    data class Parsed(
        val cleanText: String,
        val prompt: String,
        val candidates: List<ProposedWorkflow>,
    )

    private val json = Json { ignoreUnknownKeys = true; isLenient = true }
    private val fence = Regex("```(?:json)?\\s*(.*?)```", RegexOption.DOT_MATCHES_ALL)

    fun parse(content: String): Parsed = runCatching {
        val match = fence.find(content) ?: return@runCatching none(content)
        val obj = json.parseToJsonElement(match.groupValues[1].trim()).jsonObject
        val array = obj["propose_workflows"]?.jsonArray ?: return@runCatching none(content)
        val candidates = array.mapNotNull { element ->
            val item = element as? JsonObject ?: return@mapNotNull null
            val id = (item["id"] ?: item["workflowId"])?.jsonPrimitive?.contentOrNull ?: return@mapNotNull null
            ProposedWorkflow(workflowId = id, reason = item["reason"]?.jsonPrimitive?.contentOrNull ?: "")
        }
        if (candidates.isEmpty()) return@runCatching none(content)
        val prompt = obj["prompt"]?.jsonPrimitive?.contentOrNull ?: ""
        val clean = content.removeRange(match.range).trim()
        Parsed(cleanText = clean.ifBlank { prompt }, prompt = prompt, candidates = candidates)
    }.getOrElse { none(content) }

    private fun none(content: String) = Parsed(cleanText = content, prompt = "", candidates = emptyList())
}
