import "../foundation/el/elp/src/elp.el"

// elp-input.el — Convert free text → semantic frame for ELP input parsing.
//
// This is lightweight NLU: extracts predicate, arguments, and topic
// from a user message without calling an LLM. Covers the common cases:
//   - "What is X?" → predicate=tell, arg=X
//   - "Tell me about X" → predicate=tell, arg=X
//   - "How do you feel about X?" → predicate=express, arg=X
//   - "Remember X" → predicate=store, arg=X
//   - "Who is X?" → predicate=identify, arg=X
//   - "Why X?" → predicate=explain, arg=X
//   - fallback → predicate=tell, arg=full message as topic

fn elp_extract_topic(msg: String) -> String {
    // Strip common question prefixes to get the core topic
    let m1: String = if str_starts_with(msg, "What is ") { str_slice(msg, 8, str_len(msg)) } else { msg }
    let m2: String = if str_starts_with(m1, "What are ") { str_slice(m1, 9, str_len(m1)) } else { m1 }
    let m3: String = if str_starts_with(m2, "Tell me about ") { str_slice(m2, 14, str_len(m2)) } else { m2 }
    let m4: String = if str_starts_with(m3, "Who is ") { str_slice(m3, 7, str_len(m3)) } else { m3 }
    let m5: String = if str_starts_with(m4, "Who are ") { str_slice(m4, 8, str_len(m4)) } else { m4 }
    let m6: String = if str_starts_with(m5, "How do you ") { str_slice(m5, 11, str_len(m5)) } else { m5 }
    let m7: String = if str_starts_with(m6, "Why ") { str_slice(m6, 4, str_len(m6)) } else { m6 }
    let m8: String = if str_starts_with(m7, "Explain ") { str_slice(m7, 8, str_len(m7)) } else { m7 }
    // Strip trailing punctuation
    let last: Int = str_len(m8) - 1
    let trail: String = str_slice(m8, last, str_len(m8))
    let clean: String = if str_eq(trail, "?") || str_eq(trail, ".") || str_eq(trail, "!") {
        str_slice(m8, 0, last)
    } else {
        m8
    }
    return clean
}

fn elp_detect_predicate(msg: String) -> String {
    if str_starts_with(msg, "What is ") || str_starts_with(msg, "What are ") || str_starts_with(msg, "Tell me about ") {
        return "tell"
    }
    if str_starts_with(msg, "Who is ") || str_starts_with(msg, "Who are ") {
        return "identify"
    }
    if str_starts_with(msg, "Why ") || str_starts_with(msg, "Explain ") {
        return "explain"
    }
    if str_starts_with(msg, "How do you feel") || str_starts_with(msg, "Do you ") {
        return "express"
    }
    if str_starts_with(msg, "Remember ") || str_starts_with(msg, "Store ") {
        return "store"
    }
    return "tell"
}

fn elp_parse(msg: String) -> String {
    let predicate: String = elp_detect_predicate(msg)
    let topic: String = elp_extract_topic(msg)
    let safe_topic: String = str_replace(topic, "\"", "'")
    return "{\"predicate\":\"" + predicate + "\",\"args\":[\"" + safe_topic + "\"],\"modifiers\":[],\"context\":{}}"
}

fn handle_elp_chat(body: String) -> String {
    let message: String = json_get(body, "message")
    if str_eq(message, "") {
        return "{\"error\":\"message required\",\"response\":\"\"}"
    }

    let frame: String = elp_parse(message)
    let predicate: String = elp_detect_predicate(message)
    let topic: String = elp_extract_topic(message)

    // ── Layer 1: Activate ────────────────────────────────────────────────────
    // Graph walk from the extracted topic. Falls back to full message, then
    // to a shallow scan if both activation paths return empty.
    let from_topic: String = engram_activate_json(topic, 10)
    let topic_ok: Bool = !str_eq(from_topic, "") && !str_eq(from_topic, "[]")

    let candidates: String = if topic_ok {
        from_topic
    } else {
        let from_msg: String = engram_activate_json(message, 10)
        let msg_ok: Bool = !str_eq(from_msg, "") && !str_eq(from_msg, "[]")
        if msg_ok { from_msg } else { engram_scan_nodes_json(5, 0) }
    }

    // ── Layer 2: Suppress / Filter ───────────────────────────────────────────
    // Walk the candidates keeping nodes that have non-zero salience or
    // importance. Always keep at least one (the top activation hit) even if
    // all metrics are zero. Cap at 3 nodes — enough for a coherent reply.
    let total: Int = json_array_len(candidates)
    let fi: Int = 0
    let kept_count: Int = 0
    let kept_json: String = ""
    while fi < total {
        let n: String = json_array_get(candidates, fi)
        let sal_str: String = json_get(n, "salience")
        let imp_str: String = json_get(n, "importance")
        let sal_ok: Bool = !str_eq(sal_str, "0") && !str_eq(sal_str, "0.0") && !str_eq(sal_str, "")
        let imp_ok: Bool = !str_eq(imp_str, "0") && !str_eq(imp_str, "0.0") && !str_eq(imp_str, "")
        let keep_it: Bool = sal_ok || imp_ok || kept_count == 0
        if keep_it && kept_count < 3 {
            let sep: String = if str_eq(kept_json, "") { "" } else { "," }
            let kept_json = kept_json + sep + n
            let kept_count = kept_count + 1
        }
        let fi = fi + 1
    }
    let frame_nodes: String = if str_eq(kept_json, "") { "[]" } else { "[" + kept_json + "]" }

    // ── Reason ───────────────────────────────────────────────────────────────
    // Walk frame_nodes to find the first node whose content contains the topic.
    // This prevents the always-high-salience CGI architecture node from
    // dominating every response regardless of what was asked.
    let fn_total: Int = json_array_len(frame_nodes)
    let fn_i: Int = 0
    let topic_lower: String = str_to_lower(topic)
    let found_node: String = ""
    while fn_i < fn_total {
        let candidate: String = json_array_get(frame_nodes, fn_i)
        let cand_content: String = json_get(candidate, "content")
        let cand_lower: String = str_to_lower(cand_content)
        let matches: Bool = str_contains(cand_lower, topic_lower)
        if matches && str_eq(found_node, "") {
            let found_node = candidate
        }
        let fn_i = fn_i + 1
    }
    let top_node: String = if str_eq(found_node, "") { json_array_get(frame_nodes, 0) } else { found_node }
    let top_raw: String = json_get(top_node, "content")
    let patient_raw: String = if str_eq(top_raw, "") { topic } else {
        if str_len(top_raw) > 200 { str_slice(top_raw, 0, 200) } else { top_raw }
    }
    let patient_safe: String = str_replace(str_replace(patient_raw, "\"", "'"), "\n", " ")

    // ── Generate ─────────────────────────────────────────────────────────────
    // Map ELP predicate → intent, then realize through the ELP grammar engine.
    let intent_val: String = if str_eq(predicate, "store") { "command" } else { "assert" }
    let gen_form: String = "{\"intent\":\"" + intent_val + "\""
        + ",\"agent\":\"I\""
        + ",\"predicate\":\"" + predicate + "\""
        + ",\"patient\":\"" + patient_safe + "\""
        + ",\"tense\":\"present\",\"aspect\":\"simple\",\"lang\":\"en\"}"
    let realized: String = generate(gen_form)

    let response: String = if str_eq(realized, "") {
        if str_eq(patient_safe, "") {
            "Nothing in the engram matched that query."
        } else {
            patient_safe
        }
    } else {
        realized
    }
    let safe_resp: String = str_replace(str_replace(response, "\"", "'"), "\r", "")
    return "{\"response\":\"" + safe_resp + "\",\"model\":\"elp-native\",\"frame\":" + frame + ",\"nodes\":" + frame_nodes + "}"
}
