// ╔══════════════════════════════════════════════════════════════════════════════╗
// ║         ENGRAM DATA STUDIO — Native El Application                          ║
// ║         Full-featured terminal explorer for the Engram knowledge graph       ║
// ╚══════════════════════════════════════════════════════════════════════════════╝
//
// Usage: el run-file studio.el
//        ENGRAM_URL=http://host:port el run-file studio.el
//
// Requires: Engram server running (default: http://localhost:8340)

// ── Configuration ─────────────────────────────────────────────────────────────

fn get_base_url() -> String {
    let e: String = env("ENGRAM_URL")
    if e == "" {
        return "http://localhost:8340"
    }
    return e
}

fn get_report_path() -> String {
    let e: String = env("ENGRAM_REPORT")
    if e == "" {
        return "/tmp/engram-studio-report.txt"
    }
    return e
}

// ── Box-drawing helpers ────────────────────────────────────────────────────────

fn repeat_str(s: String, n: Int) -> String {
    let result: String = ""
    let i: Int = 0
    while i < n {
        let result: String = result + s
        let i: Int = i + 1
    }
    return result
}

fn hline(width: Int) -> String {
    return repeat_str("─", width)
}

fn box_top(width: Int) -> String {
    return "┌" + hline(width) + "┐"
}

fn box_bot(width: Int) -> String {
    return "└" + hline(width) + "┘"
}

fn box_row(content: String, width: Int) -> String {
    let padded: String = str_pad_right(content, width, " ")
    return "│" + padded + "│"
}

fn double_top(width: Int) -> String {
    return "╔" + repeat_str("═", width) + "╗"
}

fn double_bot(width: Int) -> String {
    return "╚" + repeat_str("═", width) + "╝"
}

fn double_row(content: String, width: Int) -> String {
    let padded: String = str_pad_right(content, width, " ")
    return "║" + padded + "║"
}

fn section_header(title: String) -> String {
    println("")
    println(color_bold(box_top(62)))
    println(color_bold(box_row("  " + title, 62)))
    println(color_bold(box_bot(62)))
    return ""
}

fn divider() -> String {
    println(color_dim("  " + repeat_str("·", 58)))
    return ""
}

// ── API access ────────────────────────────────────────────────────────────────

fn api_get(path: String) -> String {
    let url: String = get_base_url() + path
    let resp: String = http_get(url)
    if str_starts_with(resp, "{\"error\"") {
        return ""
    }
    return resp
}

fn api_post(path: String, body: String) -> String {
    let url: String = get_base_url() + path
    let resp: String = http_post(url, body)
    if str_starts_with(resp, "{\"error\"") {
        return ""
    }
    return resp
}

// ── Safe JSON field extractors ─────────────────────────────────────────────────
// These handle both raw JSON strings and parsed Value::Struct objects.

fn safe_str(node: String, key: String) -> String {
    return json_get_string(node, key)
}

fn safe_int(node: String, key: String) -> Int {
    return json_get_int(node, key)
}

fn safe_float(node: String, key: String) -> Float {
    return json_get_float(node, key)
}

// ── Formatting helpers ────────────────────────────────────────────────────────

fn short_id(full_id: String) -> String {
    if str_len(full_id) >= 8 {
        return str_slice(full_id, 0, 8)
    }
    return full_id
}

fn format_bytes(n: Int) -> String {
    if n < 1024 {
        return int_to_str(n) + " B"
    }
    if n < 1048576 {
        let kb: Int = n / 1024
        return int_to_str(kb) + " KB"
    }
    let mb: Int = n / 1048576
    return int_to_str(mb) + " MB"
}

fn format_timestamp(ms: Int) -> String {
    if ms <= 0 {
        return "—"
    }
    let secs: Int = ms / 1000
    let ts: Int = time_from_parts(secs, 0, "UTC")
    return time_format(ts, "ISO")
}

fn content_preview(content: String, max_len: Int) -> String {
    let n: Int = str_len(content)
    if n == 0 {
        return color_dim("(no content)")
    }
    if n <= max_len {
        return content
    }
    return str_slice(content, 0, max_len) + color_dim("…")
}

fn salience_bar(salience: Float) -> String {
    let pct: Float = salience * 10.0
    let filled: Int = float_to_int(pct)
    let bar: String = repeat_str("█", filled) + repeat_str("░", 10 - filled)
    if salience >= 0.7 {
        return color_green(bar)
    }
    if salience >= 0.4 {
        return color_yellow(bar)
    }
    return color_dim(bar)
}

fn tier_badge(tier: String) -> String {
    if tier == "Semantic" {
        return color_cyan("[Semantic  ]")
    }
    if tier == "Episodic" {
        return color_yellow("[Episodic  ]")
    }
    if tier == "Working" {
        return color_green("[Working   ]")
    }
    if tier == "Procedural" {
        return color_bold("[Procedural]")
    }
    return "[" + str_pad_right(tier, 10, " ") + "]"
}

fn type_badge(node_type: String) -> String {
    if node_type == "Memory" {
        return color_cyan("Memory    ")
    }
    if node_type == "Concept" {
        return color_green("Concept   ")
    }
    if node_type == "Event" {
        return color_yellow("Event     ")
    }
    if node_type == "Entity" {
        return color_bold("Entity    ")
    }
    if node_type == "Process" {
        return color_cyan("Process   ")
    }
    if node_type == "InternalState" {
        return color_dim("IntState  ")
    }
    return str_pad_right(node_type, 10, " ")
}

// ── Node renderer ─────────────────────────────────────────────────────────────

fn render_node_row(node_json: String, idx: Int) -> String {
    let id: String = short_id(safe_str(node_json, "id"))
    let label: String = safe_str(node_json, "label")
    let node_type: String = safe_str(node_json, "node_type")
    let tier: String = safe_str(node_json, "tier")
    let salience: Float = safe_float(node_json, "salience")

    let label_col: String = str_pad_right(label, 36, " ")
    let sal_str: String = format_float(salience, 3)

    let num: String = str_pad_left(int_to_str(idx + 1), 3, " ") + ". "
    return "  " + num + color_dim(id) + "  " + label_col + "  " + type_badge(node_type) + "  " + sal_str
}

fn render_node_detail(node_json: String) -> String {
    let id: String = safe_str(node_json, "id")
    let label: String = safe_str(node_json, "label")
    let node_type: String = safe_str(node_json, "node_type")
    let tier: String = safe_str(node_json, "tier")
    let salience: Float = safe_float(node_json, "salience")
    let importance: Float = safe_float(node_json, "importance")
    let confidence: Float = safe_float(node_json, "confidence")
    let created: Int = safe_int(node_json, "created_at")
    let updated: Int = safe_int(node_json, "updated_at")

    println("  " + color_bold("ID:         ") + id)
    println("  " + color_bold("Label:      ") + color_cyan(label))
    println("  " + color_bold("Type:       ") + node_type)
    println("  " + color_bold("Tier:       ") + tier_badge(tier))
    println("  " + color_bold("Salience:   ") + salience_bar(salience) + "  " + format_float(salience, 4))
    println("  " + color_bold("Importance: ") + format_float(importance, 4))
    println("  " + color_bold("Confidence: ") + format_float(confidence, 4))
    println("  " + color_bold("Created:    ") + color_dim(format_timestamp(created)))
    println("  " + color_bold("Updated:    ") + color_dim(format_timestamp(updated)))
    return ""
}

// ── Section: Stats Dashboard ──────────────────────────────────────────────────

fn show_stats(report: String) -> String {
    section_header("Database Statistics")
    let stats_json: String = api_get("/api/stats")
    if stats_json == "" {
        println("  " + color_red("Error: could not reach Engram server"))
        println("  Make sure the server is running: engram-server --data-dir <path>")
        return report
    }

    let node_count: Int = safe_int(stats_json, "node_count")
    let edge_count: Int = safe_int(stats_json, "edge_count")
    let avg_sal: Float = safe_float(stats_json, "avg_salience")
    let db_bytes: Int = safe_int(stats_json, "db_size_bytes")

    println("  " + color_bold("Nodes:        ") + color_cyan(int_to_str(node_count)))
    println("  " + color_bold("Edges:        ") + color_cyan(int_to_str(edge_count)))
    println("  " + color_bold("Avg Salience: ") + format_float(avg_sal, 4))
    println("  " + color_bold("DB Size:      ") + color_dim(format_bytes(db_bytes)))

    let new_report: String = report
        + "\n=== Database Statistics ===\n"
        + "Nodes:        " + int_to_str(node_count) + "\n"
        + "Edges:        " + int_to_str(edge_count) + "\n"
        + "Avg Salience: " + format_float(avg_sal, 4) + "\n"
        + "DB Size:      " + format_bytes(db_bytes) + "\n"
    return new_report
}

// ── Section: Node Browser ─────────────────────────────────────────────────────

fn show_nodes(node_type: String, limit: Int, report: String) -> String {
    let title: String = "Nodes — type: " + node_type
    section_header(title)

    let path: String = "/api/nodes?node_type=" + node_type + "&limit=" + int_to_str(limit)
    let json_str: String = api_get(path)
    if json_str == "" {
        println("  " + color_dim("No nodes found or server unreachable"))
        return report
    }

    let nodes: List = json_parse(json_str)
    let n: Int = list_len(nodes)
    if n == 0 {
        println("  " + color_dim("(no " + node_type + " nodes)"))
        return report
    }

    println("  " + color_dim("Showing " + int_to_str(n) + " nodes:"))
    println("")

    let header: String = "  " + str_pad_right("  #  ID       Label", 55, " ") + "  Type        Salience"
    println(color_dim(header))
    println(color_dim("  " + repeat_str("─", 70)))

    let i: Int = 0
    let report_section: String = "\n=== " + node_type + " Nodes ===\n"
    while i < n {
        let node: String = json_stringify(list_get(nodes, i))
        println(render_node_row(node, i))
        let label: String = safe_str(node, "label")
        let id: String = short_id(safe_str(node, "id"))
        let sal: Float = safe_float(node, "salience")
        let report_section: String = report_section + int_to_str(i + 1) + ". [" + id + "] " + label + " (sal=" + format_float(sal, 3) + ")\n"
        let i: Int = i + 1
    }

    return report + report_section
}

// ── Section: Recent Nodes ─────────────────────────────────────────────────────

fn show_recent(limit: Int, report: String) -> String {
    section_header("Recent Nodes (last " + int_to_str(limit) + ")")
    let path: String = "/api/nodes?limit=" + int_to_str(limit)
    let json_str: String = api_get(path)
    if json_str == "" {
        println("  " + color_dim("No nodes or server unreachable"))
        return report
    }

    let nodes: List = json_parse(json_str)
    let n: Int = list_len(nodes)
    if n == 0 {
        println("  " + color_dim("(database is empty)"))
        return report
    }

    println("  " + color_dim("Showing " + int_to_str(n) + " most recent nodes:"))
    println("")

    let header: String = "  " + str_pad_right("  #  ID       Label", 55, " ") + "  Type        Salience"
    println(color_dim(header))
    println(color_dim("  " + repeat_str("─", 70)))

    let i: Int = 0
    let report_section: String = "\n=== Recent Nodes ===\n"
    while i < n {
        let node: String = json_stringify(list_get(nodes, i))
        println(render_node_row(node, i))
        let label: String = safe_str(node, "label")
        let id: String = short_id(safe_str(node, "id"))
        let report_section: String = report_section + int_to_str(i + 1) + ". [" + id + "] " + label + "\n"
        let i: Int = i + 1
    }

    return report + report_section
}

// ── Section: Top Salient Nodes ────────────────────────────────────────────────

fn show_top_salient(limit: Int, report: String) -> String {
    section_header("Top " + int_to_str(limit) + " by Salience")
    let path: String = "/api/nodes?limit=" + int_to_str(limit) + "&min_salience=0.0"
    let json_str: String = api_get(path)
    if json_str == "" {
        println("  " + color_dim("No nodes or server unreachable"))
        return report
    }

    let nodes: List = json_parse(json_str)
    let n: Int = list_len(nodes)
    if n == 0 {
        println("  " + color_dim("(no nodes)"))
        return report
    }

    println("  " + color_dim("Salience ranking:"))
    println("")

    let report_section: String = "\n=== Top Salient Nodes ===\n"
    let i: Int = 0
    while i < n {
        let node: String = json_stringify(list_get(nodes, i))
        let id: String = short_id(safe_str(node, "id"))
        let label: String = safe_str(node, "label")
        let sal: Float = safe_float(node, "salience")
        let tier: String = safe_str(node, "tier")
        let bar: String = salience_bar(sal)
        let rank: String = str_pad_left(int_to_str(i + 1), 2, " ")
        println("  " + rank + ". " + bar + " " + format_float(sal, 3) + "  " + color_dim(id) + "  " + str_pad_right(label, 35, " ") + "  " + color_dim(tier))
        let report_section: String = report_section + rank + ". " + format_float(sal, 3) + "  [" + id + "]  " + label + "  " + tier + "\n"
        let i: Int = i + 1
    }

    return report + report_section
}

// ── Section: Node Detail ──────────────────────────────────────────────────────

fn show_node_detail(node_id: String, report: String) -> String {
    section_header("Node Detail — " + str_slice(node_id, 0, 8) + "...")
    let path: String = "/api/nodes/" + node_id
    let json_str: String = api_get(path)
    if json_str == "" {
        println("  " + color_red("Node not found: " + node_id))
        return report
    }

    render_node_detail(json_str)

    // Show neighbors
    let nb_path: String = "/api/neighbors/" + node_id + "?depth=2"
    let nb_json: String = api_get(nb_path)
    if nb_json != "" {
        let neighbors: List = json_parse(nb_json)
        let nb_n: Int = list_len(neighbors)
        if nb_n > 0 {
            println("")
            println("  " + color_bold("Neighbors (" + int_to_str(nb_n) + "):"))
            let j: Int = 0
            while j < nb_n {
                let nb: String = json_stringify(list_get(neighbors, j))
                // Use json_get_raw to extract nested objects as JSON strings
                let nb_node: String = json_get_raw(nb, "node")
                let nb_edge: String = json_get_raw(nb, "edge")
                let hops: Int = safe_int(nb, "hops")

                let nb_label: String = safe_str(nb_node, "label")
                let nb_id: String = short_id(safe_str(nb_node, "id"))
                let hop_str: String = str_pad_left(int_to_str(hops), 2, " ")
                let relation: String = safe_str(nb_edge, "relation")

                println("    hop " + hop_str + "  " + color_dim(nb_id) + "  " + color_cyan(relation) + "  →  " + nb_label)
                let j: Int = j + 1
            }
        }
    }

    return report + "\n=== Node Detail: " + node_id + " ===\n" + "Fetched node details\n"
}

// ── Section: Search ───────────────────────────────────────────────────────────

fn show_search(query: String, limit: Int, report: String) -> String {
    section_header("Search: \"" + query + "\"")
    let path: String = "/api/search?q=" + query + "&limit=" + int_to_str(limit)
    let json_str: String = api_get(path)
    if json_str == "" {
        println("  " + color_dim("No results or server unreachable"))
        return report
    }

    let nodes: List = json_parse(json_str)
    let n: Int = list_len(nodes)
    println("  " + color_bold("Found " + int_to_str(n) + " results:"))
    println("")

    if n == 0 {
        println("  " + color_dim("(no matches)"))
        return report + "\n=== Search: " + query + " ===\nNo results\n"
    }

    let report_section: String = "\n=== Search: " + query + " ===\n" + int_to_str(n) + " results:\n"
    let i: Int = 0
    while i < n {
        let node: String = json_stringify(list_get(nodes, i))
        println(render_node_row(node, i))
        let label: String = safe_str(node, "label")
        let report_section: String = report_section + int_to_str(i + 1) + ". " + label + "\n"
        let i: Int = i + 1
    }

    return report + report_section
}

// ── Section: Tier Browser ─────────────────────────────────────────────────────

fn show_nodes_by_tier(tier: String, limit: Int, report: String) -> String {
    let title: String = "Nodes — tier: " + tier
    section_header(title)

    let path: String = "/api/nodes?tier=" + tier + "&limit=" + int_to_str(limit)
    let json_str: String = api_get(path)
    if json_str == "" {
        println("  " + color_dim("No nodes or server unreachable"))
        return report
    }

    let nodes: List = json_parse(json_str)
    let n: Int = list_len(nodes)
    if n == 0 {
        println("  " + color_dim("(no " + tier + " tier nodes)"))
        return report
    }

    println("  " + tier_badge(tier) + "  " + color_dim("Showing " + int_to_str(n) + " nodes"))
    println("")

    let i: Int = 0
    let report_section: String = "\n=== " + tier + " Tier Nodes ===\n"
    while i < n {
        let node: String = json_stringify(list_get(nodes, i))
        println(render_node_row(node, i))
        let label: String = safe_str(node, "label")
        let id: String = short_id(safe_str(node, "id"))
        let report_section: String = report_section + "[" + id + "] " + label + "\n"
        let i: Int = i + 1
    }

    return report + report_section
}

// ── Section: Spreading Activation ────────────────────────────────────────────

fn show_activation(seed_id: String, limit: Int, report: String) -> String {
    section_header("Spreading Activation — seed: " + str_slice(seed_id, 0, 8) + "...")
    let path: String = "/api/activate?seeds=" + seed_id + "&limit=" + int_to_str(limit) + "&depth=3"
    let json_str: String = api_get(path)
    if json_str == "" {
        println("  " + color_dim("No activation results or server unreachable"))
        return report
    }

    // Use json_get_raw to get the "results" array as a JSON string, then parse it
    let results_raw: String = json_get_raw(json_str, "results")
    let results: List = json_parse(results_raw)
    let n: Int = list_len(results)
    if n == 0 {
        println("  " + color_dim("(no spreading activation results — check seed ID)"))
        return report
    }

    println("  " + color_bold("Activated " + int_to_str(n) + " nodes:"))
    println("")

    let header: String = "  " + str_pad_right("  #  ID       Label", 52, " ") + "  Strength    Hops"
    println(color_dim(header))
    println(color_dim("  " + repeat_str("─", 68)))

    let report_section: String = "\n=== Activation from " + str_slice(seed_id, 0, 8) + " ===\n"
    let i: Int = 0
    while i < n {
        let result: String = json_stringify(list_get(results, i))
        // Use json_get_raw to extract nested node object as a JSON string
        let node: String = json_get_raw(result, "node")
        let strength: Float = safe_float(result, "activation_strength")
        let hops: Int = safe_int(result, "hops")

        let id: String = short_id(safe_str(node, "id"))
        let label: String = safe_str(node, "label")

        let rank: String = str_pad_left(int_to_str(i + 1), 3, " ")
        let strength_bar: String = salience_bar(strength)
        println("  " + rank + ". " + color_dim(id) + "  " + str_pad_right(label, 36, " ") + "  " + format_float(strength, 3) + "  " + color_dim("hops:" + int_to_str(hops)))
        let report_section: String = report_section + rank + ". [" + id + "] " + label + " strength=" + format_float(strength, 3) + " hops=" + int_to_str(hops) + "\n"
        let i: Int = i + 1
    }

    return report + report_section
}

// ── Section: Edge Statistics ──────────────────────────────────────────────────

fn show_edges(limit: Int, report: String) -> String {
    section_header("Edge Explorer (sample of " + int_to_str(limit) + ")")
    let path: String = "/api/edges?limit=" + int_to_str(limit)
    let json_str: String = api_get(path)
    if json_str == "" {
        println("  " + color_dim("No edges or server unreachable"))
        return report
    }

    let edges: List = json_parse(json_str)
    let n: Int = list_len(edges)
    if n == 0 {
        println("  " + color_dim("(no edges in database)"))
        return report
    }

    println("  " + color_dim("Showing " + int_to_str(n) + " edges:"))
    println("")

    let header: String = "  " + str_pad_right("  #  From     →  To       Relation", 56, " ") + "  Weight"
    println(color_dim(header))
    println(color_dim("  " + repeat_str("─", 68)))

    let report_section: String = "\n=== Edges ===\n"
    let i: Int = 0
    while i < n {
        let edge: String = json_stringify(list_get(edges, i))
        let from_id: String = short_id(safe_str(edge, "from_id"))
        let to_id: String = short_id(safe_str(edge, "to_id"))
        let relation: String = safe_str(edge, "relation")
        let weight: Float = safe_float(edge, "weight")
        let rank: String = str_pad_left(int_to_str(i + 1), 3, " ")

        println("  " + rank + ". " + color_dim(from_id) + "  " + color_cyan("→") + "  " + color_dim(to_id) + "  " + color_green(str_pad_right(relation, 20, " ")) + "  " + format_float(weight, 3))
        let report_section: String = report_section + from_id + " -[" + relation + "]-> " + to_id + " w=" + format_float(weight, 3) + "\n"
        let i: Int = i + 1
    }

    return report + report_section
}

// ── Section: Knowledge Browser (Concept nodes grouped by tag prefix) ──────────

fn show_knowledge_browser(report: String) -> String {
    section_header("Knowledge Browser — Concept Nodes")
    let path: String = "/api/nodes?node_type=Concept&limit=50"
    let json_str: String = api_get(path)
    if json_str == "" {
        println("  " + color_dim("No concept nodes or server unreachable"))
        return report
    }

    let nodes: List = json_parse(json_str)
    let n: Int = list_len(nodes)
    if n == 0 {
        println("  " + color_dim("(no Concept nodes — these are your domain knowledge anchors)"))
        return report
    }

    println("  " + color_bold(int_to_str(n) + " concept nodes found:"))
    println("")

    let report_section: String = "\n=== Knowledge Browser ===\n"
    let i: Int = 0
    while i < n {
        let node: String = json_stringify(list_get(nodes, i))
        let id: String = short_id(safe_str(node, "id"))
        let label: String = safe_str(node, "label")
        let tier: String = safe_str(node, "tier")
        let sal: Float = safe_float(node, "salience")
        println("  " + color_green("◆") + " " + str_pad_right(label, 40, " ") + "  " + color_dim(id) + "  " + color_dim(tier) + "  " + format_float(sal, 3))
        let report_section: String = report_section + "◆ " + label + " [" + id + "] " + tier + "\n"
        let i: Int = i + 1
    }

    return report + report_section
}

// ── Section: Mode Simulation (interactive mode preview) ──────────────────────

fn show_mode_menu() -> String {
    let _ = section_header("Interactive Mode Preview")
    println("  " + color_bold("Available commands (when running interactively):"))
    println("")
    println("  " + color_cyan("stats") + "              — show database statistics")
    println("  " + color_cyan("browse <type>") + "      — browse nodes by type")
    println("      types: Memory, Concept, Event, Entity, Process, InternalState")
    println("  " + color_cyan("tier <tier>") + "        — browse nodes by tier")
    println("      tiers: Working, Episodic, Semantic, Procedural")
    println("  " + color_cyan("search <query>") + "     — full-text search")
    println("  " + color_cyan("node <id>") + "          — show node detail + neighbors")
    println("  " + color_cyan("activate <id>") + "      — spreading activation from seed")
    println("  " + color_cyan("edges") + "              — browse edges")
    println("  " + color_cyan("top") + "                — top nodes by salience")
    println("  " + color_cyan("export <path>") + "      — save text report to file")
    println("  " + color_cyan("help") + "               — show this menu")
    println("  " + color_cyan("quit") + "               — exit studio")
    println("")
    println("  " + color_dim("Tip: set ENGRAM_URL=http://host:port to connect to a different server"))
    return ""
}

// ── Export report ─────────────────────────────────────────────────────────────

fn export_report(content: String, path: String) -> String {
    let _ = section_header("Report Export")
    let header: String = "ENGRAM DATA STUDIO — Report\n"
        + "Generated: " + time_format(time_now_utc(), "ISO") + "\n"
        + "Server: " + get_base_url() + "\n"
        + repeat_str("=", 60) + "\n"
    let ok: Bool = fs_write(path, header + content)
    if ok {
        println("  " + color_green("✓") + " Report saved to: " + color_bold(path))
        println("  " + color_dim("Size: " + format_bytes(str_len(header + content))))
    } else {
        println("  " + color_red("✗") + " Failed to write report to: " + path)
    }
    return ""
}

// ── Connectivity check ────────────────────────────────────────────────────────

fn check_connection() -> Bool {
    let resp: String = api_get("/api/stats")
    return resp != ""
}

// ═══════════════════════════════════════════════════════════════════════════════
// ── MAIN STUDIO ENTRY POINT ──────────────────────────────────────────────────
// ═══════════════════════════════════════════════════════════════════════════════

let base_url: String = get_base_url()
let WIDTH: Int = 64

// ── Banner ────────────────────────────────────────────────────────────────────
println("")
println(color_bold(double_top(WIDTH)))
println(color_bold(double_row("    ███████╗███╗   ██╗ ██████╗ ██████╗  █████╗ ███╗   ███╗", WIDTH)))
println(color_bold(double_row("    ██╔════╝████╗  ██║██╔════╝ ██╔══██╗██╔══██╗████╗ ████║", WIDTH)))
println(color_bold(double_row("    █████╗  ██╔██╗ ██║██║  ███╗██████╔╝███████║██╔████╔██║", WIDTH)))
println(color_bold(double_row("    ██╔══╝  ██║╚██╗██║██║   ██║██╔══██╗██╔══██║██║╚██╔╝██║", WIDTH)))
println(color_bold(double_row("    ███████╗██║ ╚████║╚██████╔╝██║  ██║██║  ██║██║ ╚═╝ ██║", WIDTH)))
println(color_bold(double_row("    ╚══════╝╚═╝  ╚═══╝ ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝     ╚═╝", WIDTH)))
println(color_bold(double_row("", WIDTH)))
println(color_bold(double_row("           DATA STUDIO — Native El Application", WIDTH)))
println(color_bold(double_row("", WIDTH)))
println(color_bold(double_bot(WIDTH)))
println("")
println("  " + color_bold("Server:  ") + color_cyan(base_url))
println("  " + color_bold("Version: ") + color_dim("El-native · 10-loop pass"))
println("")

// ── Connection check ─────────────────────────────────────────────────────────
let connected: Bool = check_connection()
if connected {
    println("  " + color_green("●") + " " + color_bold("Connection established"))
} else {
    println("  " + color_red("●") + " " + color_bold("Server unreachable"))
    println("  " + color_dim("Start the server: ENGRAM_BIND=0.0.0.0:8340 engram-server --data-dir <path>"))
    println("  " + color_dim("Or set ENGRAM_URL environment variable"))
    println("")
    println("  " + color_yellow("Continuing in offline mode — all sections will show placeholders"))
}

// ── Run all dashboard sections ─────────────────────────────────────────────────

let report: String = ""

// Section 1: Stats
let report: String = show_stats(report)

// Section 2: Recent nodes
let report: String = show_recent(20, report)

// Section 3: Top salient
let report: String = show_top_salient(10, report)

// Section 4: By node type — Memory
let report: String = show_nodes("Memory", 10, report)

// Section 5: By node type — Concept
let report: String = show_nodes("Concept", 10, report)

// Section 6: By node type — Event
let report: String = show_nodes("Event", 8, report)

// Section 7: By node type — Entity
let report: String = show_nodes("Entity", 8, report)

// Section 8: By node type — Process
let report: String = show_nodes("Process", 8, report)

// Section 9: Tier — Semantic
let report: String = show_nodes_by_tier("Semantic", 10, report)

// Section 10: Tier — Episodic
let report: String = show_nodes_by_tier("Episodic", 10, report)

// Section 11: Knowledge browser (Concept nodes)
let report: String = show_knowledge_browser(report)

// Section 12: Text search samples
let report: String = show_search("memory", 10, report)
let report: String = show_search("concept", 8, report)

// Section 13: Edge explorer
let report: String = show_edges(15, report)

// Section 14: Interactive mode preview
show_mode_menu()

// ── Export report ──────────────────────────────────────────────────────────────
let report_path: String = get_report_path()
export_report(report, report_path)

// ── Footer ────────────────────────────────────────────────────────────────────
println("")
println(color_bold("  " + repeat_str("═", 60)))
println("  " + color_bold("Engram Data Studio") + color_dim(" — session complete"))
println("  " + color_dim(time_format(time_now_utc(), "ISO")))
println(color_bold("  " + repeat_str("═", 60)))
println("")
