/* test_m3_parity.c — M3 JSON-parity gate for the ENGRAM_STORE wiring.
 *
 * This is a REAL el-level harness: it links the actual el_runtime.o (the soul's
 * native engram builtins) + engram_store.o and calls the engram_node family plus
 * engram_connect, engram_activate_json, engram_save, engram_store_boot directly. No EL interpreter
 * and no full soul build are needed — el_runtime.c compiles to a standalone .o
 * whose engram builtins operate on the process-global engram store, and the
 * string arena is inert unless el_request_start() is called, so the builtins are
 * callable straight from C (el_val_t is int64_t; EL_STR/EL_CSTR are pointer casts).
 *
 * Modes (argv[1]), data dir (argv[2]):
 *   seed   — ENGRAM_STORE unset: build a fixed seed graph, write snapshot.json +
 *            off_graph.json (pristine, pre-activation), then activate → off_act.json.
 *   on     — ENGRAM_STORE=1: engram_store_boot(dir) imports snapshot.json ONCE into
 *            neuron.egm and loads it resident; write on_graph.json, then activate →
 *            on_act.json; checkpoint + close.
 *   reboot — ENGRAM_STORE=1 with snapshot.json DELETED: boot must reload from
 *            neuron.egm (WAL replay), never re-reading JSON; write reboot_graph.json.
 *   offcheck — assert flag-off leaves the store untouched.
 *
 * The graph comparison (done by run_m3_parity.sh via python, modulo ordering) is
 * the deterministic gate; activation ids/promoted are compared as a robust set.
 */
#include "el_runtime.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Builtins the header declares are pulled in via el_runtime.h. The M3 additions
 * are not in the header yet, so declare them here. */
extern int      engram_store_enabled(void);
extern el_val_t engram_store_boot(el_val_t data_dir);
extern el_val_t engram_store_checkpoint(void);
extern el_val_t engram_store_close(void);
extern el_val_t engram_node_layered(el_val_t content, el_val_t node_type, el_val_t label,
                                    el_val_t salience, el_val_t certainty, el_val_t confidence,
                                    el_val_t status, el_val_t tags, el_val_t layer_id);

static el_val_t S(const char* s){ return EL_STR(s); }
static el_val_t F(double d){ return el_from_float(d); }

/* Build a fixed, deterministic seed graph: 12 nodes across two layers + 9 edges.
 * Content is chosen so an activation query has real matches to rank. */
static void build_seed(void){
    /* core-identity layer (1) via engram_node_full */
    el_val_t n0  = engram_node_full(S("tiered storage engine design"), S("Concept"),
                     S("storage-engine"), F(0.9), F(0.8), F(1.0), S("Semantic"), S("design,storage"));
    el_val_t n1  = engram_node_full(S("write-ahead log durability"), S("Concept"),
                     S("wal"), F(0.85), F(0.75), F(1.0), S("Semantic"), S("wal,durability"));
    el_val_t n2  = engram_node_full(S("paged buffer pool with checkpointing"), S("Concept"),
                     S("buffer-pool"), F(0.8), F(0.7), F(1.0), S("Semantic"), S("paging"));
    el_val_t n3  = engram_node_full(S("spreading activation over the graph"), S("Concept"),
                     S("activation"), F(0.8), F(0.7), F(1.0), S("Semantic"), S("activation,graph"));
    el_val_t n4  = engram_node_full(S("hebbian co-activation potentiation"), S("Concept"),
                     S("hebbian"), F(0.7), F(0.6), F(1.0), S("Semantic"), S("hebb"));
    el_val_t n5  = engram_node_full(S("crash recovery replays the log"), S("Concept"),
                     S("recovery"), F(0.75), F(0.65), F(1.0), S("Semantic"), S("recovery,wal"));
    /* domain-knowledge layer (2) via engram_node_layered */
    el_val_t n6  = engram_node_layered(S("b-tree primary index id to location"), S("Fact"),
                     S("btree"), F(0.7), F(0.6), F(1.0), S(""), S("index"), (el_val_t)2);
    el_val_t n7  = engram_node_layered(S("adjacency index for edge lookup"), S("Fact"),
                     S("adjacency"), F(0.7), F(0.6), F(1.0), S(""), S("index,graph"), (el_val_t)2);
    el_val_t n8  = engram_node_layered(S("slotted pages hold tlv records"), S("Fact"),
                     S("slotted-page"), F(0.65), F(0.55), F(1.0), S(""), S("format"), (el_val_t)2);
    el_val_t n9  = engram_node_full(S("memory tiers working semantic episodic"), S("Concept"),
                     S("tiers"), F(0.7), F(0.6), F(1.0), S("Semantic"), S("tiers,memory"));
    el_val_t n10 = engram_node_full(S("embeddings enable nearest neighbour search"), S("Concept"),
                     S("embeddings"), F(0.65), F(0.55), F(1.0), S("Semantic"), S("embeddings"));
    el_val_t n11 = engram_node_full(S("the durable engram is the mind's memory"), S("Belief"),
                     S("engram"), F(0.95), F(0.9), F(1.0), S("Semantic"), S("engram,memory"));

    engram_connect(n0, n1, F(0.8), S("depends-on"));
    engram_connect(n0, n2, F(0.8), S("depends-on"));
    engram_connect(n0, n3, F(0.7), S("enables"));
    engram_connect(n1, n5, F(0.9), S("enables"));
    engram_connect(n3, n4, F(0.6), S("triggers"));
    engram_connect(n2, n6, F(0.7), S("uses"));
    engram_connect(n3, n7, F(0.7), S("uses"));
    engram_connect(n0, n8, F(0.6), S("uses"));
    engram_connect(n11, n9, F(0.8), S("about"));
    engram_connect(n11, n10, F(0.5), S("about"));
}

static void write_file(const char* path, const char* content){
    FILE* f = fopen(path, "wb");
    if (!f){ fprintf(stderr, "cannot open %s\n", path); exit(2); }
    if (content) fwrite(content, 1, strlen(content), f);
    fclose(f);
}

static const char* QUERY = "storage engine activation and the durable log";

int main(int argc, char** argv){
    if (argc < 3){ fprintf(stderr, "usage: %s <seed|on|reboot|offcheck> <dir>\n", argv[0]); return 2; }
    const char* mode = argv[1];
    const char* dir  = argv[2];
    char p[1024];

    if (!strcmp(mode, "seed")){
        if (engram_store_enabled()){ fprintf(stderr, "seed mode requires ENGRAM_STORE unset\n"); return 2; }
        build_seed();
        snprintf(p, sizeof p, "%s/snapshot.json", dir);
        if (!engram_save(S(p))){ fprintf(stderr, "seed save failed\n"); return 2; }
        snprintf(p, sizeof p, "%s/off_graph.json", dir);
        engram_save(S(p));                         /* pristine off-path graph */
        el_val_t act = engram_activate_json(S(QUERY), (el_val_t)3);
        snprintf(p, sizeof p, "%s/off_act.json", dir);
        write_file(p, EL_CSTR(act));
        printf("[seed] nodes=%lld edges=%lld\n",
               (long long)(int64_t)engram_node_count(), (long long)(int64_t)engram_edge_count());
        return 0;
    }
    if (!strcmp(mode, "on")){
        if (!engram_store_enabled()){ fprintf(stderr, "on mode requires ENGRAM_STORE=1\n"); return 2; }
        if (!engram_store_boot(S(dir))){ fprintf(stderr, "store boot failed\n"); return 2; }
        snprintf(p, sizeof p, "%s/on_graph.json", dir);
        engram_save(S(p));                         /* export resident (== store) */
        /* Checkpoint the freshly-imported (pristine) graph — this is the state
         * the reboot comparison expects to round-trip. Under M3.5 a checkpoint
         * persists the resident graph's CURRENT field state, so it must run
         * BEFORE activation mutates fields in place; activation itself is
         * exercised below only for the activation-result-set parity check. The
         * M3.5 gate (test_m35_hebb_persist) separately proves that a checkpoint
         * taken AFTER activation durably carries the learned hebb/WM state. */
        engram_store_checkpoint();
        el_val_t act = engram_activate_json(S(QUERY), (el_val_t)3);
        snprintf(p, sizeof p, "%s/on_act.json", dir);
        write_file(p, EL_CSTR(act));
        printf("[on] nodes=%lld edges=%lld\n",
               (long long)(int64_t)engram_node_count(), (long long)(int64_t)engram_edge_count());
        engram_store_close();
        return 0;
    }
    if (!strcmp(mode, "reboot")){
        if (!engram_store_enabled()){ fprintf(stderr, "reboot mode requires ENGRAM_STORE=1\n"); return 2; }
        /* snapshot.json has been deleted by the runner — boot MUST come from
         * neuron.egm (+ WAL replay), never re-reading JSON. */
        if (!engram_store_boot(S(dir))){ fprintf(stderr, "reboot boot failed\n"); return 2; }
        snprintf(p, sizeof p, "%s/reboot_graph.json", dir);
        engram_save(S(p));
        printf("[reboot] nodes=%lld edges=%lld\n",
               (long long)(int64_t)engram_node_count(), (long long)(int64_t)engram_edge_count());
        engram_store_close();
        return 0;
    }
    if (!strcmp(mode, "offcheck")){
        /* ENGRAM_STORE unset: enabled()==0 and boot is a no-op returning 0. */
        int en = engram_store_enabled();
        el_val_t b = engram_store_boot(S(dir));
        printf("[offcheck] enabled=%d boot_ret=%lld\n", en, (long long)(int64_t)b);
        return (en == 0 && (int64_t)b == 0) ? 0 : 1;
    }
    fprintf(stderr, "unknown mode %s\n", mode);
    return 2;
}
