package com.correx.core.kernel.orchestration

import com.correx.core.events.events.BriefEcho
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive

/**
 * Extracts the structured [BriefEcho] from two artifact shapes the echo gate compares:
 *
 * - [fromEcho]: a planner's produced artifact, which restates the brief under a `brief_echo` key:
 *   `{"brief_echo": {"referenced_files": [...], "symbols": [...], "acceptance_criteria": [...]}}`.
 * - [fromOriginalBrief]: the original (analyst) brief artifact, whose three dimensions are pulled
 *   best-effort — referenced files via [BriefReferenceExtractor], plus optional `acceptance_criteria`
 *   and `symbols` arrays.
 *
 * Both are deterministic and tolerant of missing keys, parsing with kotlinx-serialization Json
 * like [BriefReferenceExtractor].
 */
internal object BriefEchoExtractor {

    private const val BRIEF_ECHO_KEY = "brief_echo"
    private const val REFERENCED_FILES_KEY = "referenced_files"
    private const val SYMBOLS_KEY = "symbols"
    private const val ACCEPTANCE_CRITERIA_KEY = "acceptance_criteria"

    /** Returns the `brief_echo` block as a [BriefEcho], or null if the artifact has no such key. */
    fun fromEcho(artifactJson: String): BriefEcho? {
        val root = artifactJson.parseObjectOrNull() ?: return null
        val echo = root[BRIEF_ECHO_KEY] as? JsonObject ?: return null
        return BriefEcho(
            referencedFiles = echo.stringSet(REFERENCED_FILES_KEY),
            symbols = echo.stringSet(SYMBOLS_KEY),
            acceptanceCriteria = echo.stringSet(ACCEPTANCE_CRITERIA_KEY),
        )
    }

    /** The same three dimensions pulled from the original brief artifact, best-effort. */
    fun fromOriginalBrief(artifactJson: String): BriefEcho {
        val root = artifactJson.parseObjectOrNull()
        return BriefEcho(
            referencedFiles = BriefReferenceExtractor.fileReferences(artifactJson).toSet(),
            symbols = root?.stringSet(SYMBOLS_KEY) ?: emptySet(),
            acceptanceCriteria = root?.stringSet(ACCEPTANCE_CRITERIA_KEY) ?: emptySet(),
        )
    }

    private fun String.parseObjectOrNull(): JsonObject? =
        runCatching { Json.parseToJsonElement(this) }.getOrNull() as? JsonObject

    /** Non-blank string leaves of the named array, in order; empty when the key is absent/non-array. */
    private fun JsonObject.stringSet(key: String): Set<String> {
        val array = this[key] as? JsonArray ?: return emptySet()
        return array.mapNotNullTo(LinkedHashSet()) { it.stringOrNull()?.takeIf(String::isNotBlank) }
    }

    private fun JsonElement.stringOrNull(): String? =
        (this as? JsonPrimitive)?.takeIf { it.isString }?.content
}
