// epm/src/registry.el — Engram-backed vessel registry
//
// Vessels are stored in Engram as nodes with a structured label scheme:
//
//   label:     "vessel:<name>:<version>"   (used as the search key)
//   content:   JSON blob with full vessel metadata
//   node_type: "Entity"
//   salience:  0.9  (vessels are high-salience, long-lived knowledge)
//
// All registry operations go over HTTP to Engram. The Engram URL is read
// from the ENGRAM_URL environment variable; defaults to http://localhost:8742.
//
// Endpoints used:
//   POST /api/nodes          — publish a vessel node
//   GET  /api/search?q=...   — find vessels by label prefix

// ── Engram URL ────────────────────────────────────────────────────────────────

// registry_url returns the base URL for Engram, with no trailing slash.
fn registry_url() -> String {
    let u: String = config("ENGRAM_URL")
    if str_eq(u, "") { return "http://localhost:8742" }
    // Strip trailing slash if present
    let n: Int = str_len(u)
    if str_ends_with(u, "/") {
        return str_slice(u, 0, n - 1)
    }
    return u
}

// ── Publish ───────────────────────────────────────────────────────────────────

// registry_publish stores a vessel node in Engram.
//
// Parameters:
//   name        — vessel name (e.g. "el-auth")
//   version     — semver string (e.g. "0.1.0")
//   description — human-readable description
//   entry       — build entry file path
//   deps_json   — JSON array of dep objects (from manifest_deps)
//
// Returns the created node ID on success, "" on failure.
fn registry_publish(name: String, version: String, description: String, entry: String, deps_json: String) -> String {
    let label: String = "vessel:" + name + ":" + version

    // Build the content JSON — all vessel metadata in one blob
    let esc_name: String = json_escape_string(name)
    let esc_ver: String  = json_escape_string(version)
    let esc_desc: String = json_escape_string(description)
    let esc_entry: String = json_escape_string(entry)
    let content_json: String = "{\"name\":\"" + esc_name + "\",\"version\":\"" + esc_ver + "\",\"description\":\"" + esc_desc + "\",\"entry\":\"" + esc_entry + "\",\"deps\":" + deps_json + "}"

    // Escape the content blob for embedding in the outer JSON
    let esc_label: String = json_escape_string(label)
    let esc_content: String = json_escape_string(content_json)

    let body: String = "{\"label\":\"" + esc_label + "\",\"content\":\"" + esc_content + "\",\"node_type\":\"Entity\",\"salience\":0.9}"
    let url: String  = registry_url() + "/api/nodes"

    let resp: String = http_post_json(url, body)
    if str_eq(resp, "") {
        println("epm: error: Engram unreachable at " + url)
        return ""
    }

    // Check for error in response
    let err: String = json_get_string(resp, "error")
    if !str_eq(err, "") {
        println("epm: error from Engram: " + err)
        return ""
    }

    let node_id: String = json_get_string(resp, "id")
    return node_id
}

// ── Find ──────────────────────────────────────────────────────────────────────

// registry_find searches Engram for a specific vessel by name and version.
//
// Returns the content JSON blob for the vessel, or "" if not found.
// When version is "" any version matching the name is accepted (first hit).
fn registry_find(name: String, version: String) -> String {
    let query: String = "vessel:" + name
    if !str_eq(version, "") {
        let query = "vessel:" + name + ":" + version
    }

    let enc_q: String = url_encode(query)
    let url: String = registry_url() + "/api/search?q=" + enc_q + "&limit=10"

    let resp: String = http_get(url)
    if str_eq(resp, "") {
        println("epm: error: Engram unreachable at " + registry_url())
        return ""
    }

    // Response is a JSON array of node objects
    let count: Int = json_array_len(resp)
    if count == 0 { return "" }

    // Walk results to find exact match
    let i: Int = 0
    while i < count {
        let node: String = json_array_get(resp, i)
        let node_label: String = json_get_string(node, "label")
        let expected_label: String = "vessel:" + name
        if !str_eq(version, "") {
            let expected_label = "vessel:" + name + ":" + version
        }
        if str_eq(node_label, expected_label) {
            // The content field is an escaped JSON blob — decode it
            let raw_content: String = json_get_string(node, "content")
            return raw_content
        }
        let i = i + 1
    }

    // No exact match; if version was unspecified return first result's content
    if str_eq(version, "") {
        let first: String = json_array_get(resp, 0)
        let first_label: String = json_get_string(first, "label")
        if str_starts_with(first_label, "vessel:" + name + ":") {
            return json_get_string(first, "content")
        }
    }

    return ""
}

// ── List ──────────────────────────────────────────────────────────────────────

// registry_list returns all vessels from Engram as a JSON array.
//
// Each element in the returned array is a content JSON blob.
// Returns "[]" when no vessels are found or Engram is unreachable.
fn registry_list() -> String {
    let enc_q: String = url_encode("vessel:")
    let url: String = registry_url() + "/api/search?q=" + enc_q + "&limit=200"

    let resp: String = http_get(url)
    if str_eq(resp, "") {
        println("epm: error: Engram unreachable at " + registry_url())
        return "[]"
    }

    let count: Int = json_array_len(resp)
    if count == 0 { return "[]" }

    // Collect content blobs for nodes whose label starts with "vessel:"
    let out: String = "["
    let added: Int = 0
    let i: Int = 0
    while i < count {
        let node: String = json_array_get(resp, i)
        let lbl: String  = json_get_string(node, "label")
        if str_starts_with(lbl, "vessel:") {
            let content: String = json_get_string(node, "content")
            let esc: String = json_escape_string(content)
            if added == 0 {
                let out = out + "\"" + esc + "\""
            } else {
                let out = out + ",\"" + esc + "\""
            }
            let added = added + 1
        }
        let i = i + 1
    }
    return out + "]"
}
