#include <stdint.h>
#include <stdlib.h>
#include "el_runtime.h"

el_val_t parse_port(el_val_t bind);
el_val_t ok_json(void);
el_val_t err_json(el_val_t msg);
el_val_t strip_query(el_val_t path);
el_val_t query_param(el_val_t path, el_val_t key);
el_val_t query_int(el_val_t path, el_val_t key, el_val_t default_val);
el_val_t extract_id(el_val_t path, el_val_t prefix);
el_val_t route_stats(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_create_node(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_get_node(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_scan_nodes(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_scan_edges(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_search(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_activate(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_create_edge(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_neighbors(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_strengthen(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_forget(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_create_ise(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_sync(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_save(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_load(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_health(el_val_t method, el_val_t path, el_val_t body);
el_val_t check_auth_ok(el_val_t method, el_val_t body);
el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body);

el_val_t bind_str;
el_val_t port;
el_val_t data_dir;
el_val_t snapshot_path;

el_val_t parse_port(el_val_t bind) {
  el_val_t colon = str_index_of(bind, EL_STR(":"));
  if (colon < 0) {
    return str_to_int(bind);
  }
  el_val_t after = str_slice(bind, (colon + 1), str_len(bind));
  return str_to_int(after);
  return 0;
}

el_val_t ok_json(void) {
  return EL_STR("{\"ok\":true}");
  return 0;
}

el_val_t err_json(el_val_t msg) {
  return el_str_concat(el_str_concat(EL_STR("{\"error\":\""), msg), EL_STR("\"}"));
  return 0;
}

el_val_t strip_query(el_val_t path) {
  el_val_t q = str_index_of(path, EL_STR("?"));
  if (q < 0) {
    return path;
  }
  return str_slice(path, 0, q);
  return 0;
}

el_val_t query_param(el_val_t path, el_val_t key) {
  el_val_t q = str_index_of(path, EL_STR("?"));
  if (q < 0) {
    return EL_STR("");
  }
  el_val_t qs = str_slice(path, (q + 1), str_len(path));
  el_val_t needle = el_str_concat(key, EL_STR("="));
  el_val_t pos = str_index_of(qs, needle);
  if (pos < 0) {
    return EL_STR("");
  }
  el_val_t after = str_slice(qs, (pos + str_len(needle)), str_len(qs));
  el_val_t amp = str_index_of(after, EL_STR("&"));
  if (amp < 0) {
    return after;
  }
  return str_slice(after, 0, amp);
  return 0;
}

el_val_t query_int(el_val_t path, el_val_t key, el_val_t default_val) {
  el_val_t v = query_param(path, key);
  if (str_eq(v, EL_STR(""))) {
    return default_val;
  }
  return str_to_int(v);
  return 0;
}

el_val_t extract_id(el_val_t path, el_val_t prefix) {
  el_val_t clean = strip_query(path);
  if (!str_starts_with(clean, prefix)) {
    return EL_STR("");
  }
  el_val_t after = str_slice(clean, str_len(prefix), str_len(clean));
  el_val_t slash = str_index_of(after, EL_STR("/"));
  if (slash < 0) {
    return after;
  }
  return str_slice(after, 0, slash);
  return 0;
}

el_val_t route_stats(el_val_t method, el_val_t path, el_val_t body) {
  return engram_stats_json();
  return 0;
}

el_val_t route_create_node(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t content = json_get_string(body, EL_STR("content"));
  el_val_t node_type = json_get_string(body, EL_STR("node_type"));
  if (str_eq(node_type, EL_STR(""))) {
    node_type = EL_STR("Memory");
  }
  el_val_t salience = json_get_float(body, EL_STR("salience"));
  if (salience == el_from_float(0.0)) {
    salience = el_from_float(0.5);
  }
  el_val_t id = engram_node(content, node_type, salience);
  return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"content\":\"")), content), EL_STR("\",\"node_type\":\"")), node_type), EL_STR("\"}"));
  return 0;
}

el_val_t route_get_node(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t id = extract_id(path, EL_STR("/api/nodes/"));
  if (str_eq(id, EL_STR(""))) {
    return err_json(EL_STR("missing id"));
  }
  return engram_get_node_json(id);
  return 0;
}

el_val_t route_scan_nodes(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t limit = query_int(path, EL_STR("limit"), 50);
  el_val_t offset = query_int(path, EL_STR("offset"), 0);
  el_val_t nt = query_param(path, EL_STR("node_type"));
  if (str_eq(nt, EL_STR(""))) {
    return engram_scan_nodes_json(limit, offset);
  }
  return engram_scan_nodes_by_type_json(nt, limit, offset);
  return 0;
}

el_val_t route_scan_edges(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t dir = env(EL_STR("ENGRAM_DATA_DIR"));
  if (str_eq(dir, EL_STR(""))) {
    dir = EL_STR("/tmp/engram");
  }
  el_val_t snap_path = el_str_concat(dir, EL_STR("/snapshot.json"));
  engram_save(snap_path);
  el_val_t snap = fs_read(snap_path);
  if (str_eq(snap, EL_STR(""))) {
    return EL_STR("[]");
  }
  el_val_t edges = json_get_raw(snap, EL_STR("edges"));
  if (str_eq(edges, EL_STR(""))) {
    return EL_STR("[]");
  }
  return edges;
  return 0;
}

el_val_t route_search(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t q = EL_STR("");
  if (str_eq(method, EL_STR("GET"))) {
    q = query_param(path, EL_STR("q"));
  } else {
    q = json_get_string(body, EL_STR("query"));
  }
  el_val_t limit = query_int(path, EL_STR("limit"), 20);
  if (limit == 0) {
    limit = json_get_int(body, EL_STR("limit"));
  }
  if (limit == 0) {
    limit = 20;
  }
  return engram_search_json(q, limit);
  return 0;
}

el_val_t route_activate(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t q = EL_STR("");
  el_val_t depth = 3;
  if (str_eq(method, EL_STR("GET"))) {
    q = query_param(path, EL_STR("q"));
    depth = query_int(path, EL_STR("depth"), 3);
  } else {
    q = json_get_string(body, EL_STR("query"));
    el_val_t bd = json_get_int(body, EL_STR("depth"));
    if (bd > 0) {
      depth = bd;
    }
  }
  return el_str_concat(el_str_concat(EL_STR("{\"results\":"), engram_activate_json(q, depth)), EL_STR("}"));
  return 0;
}

el_val_t route_create_edge(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t from_id = json_get_string(body, EL_STR("from_id"));
  el_val_t to_id = json_get_string(body, EL_STR("to_id"));
  el_val_t relation = json_get_string(body, EL_STR("relation"));
  if (str_eq(relation, EL_STR(""))) {
    relation = EL_STR("associates");
  }
  el_val_t weight = json_get_float(body, EL_STR("weight"));
  if (weight == el_from_float(0.0)) {
    weight = el_from_float(0.5);
  }
  engram_connect(from_id, to_id, weight, relation);
  return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"from_id\":\""), from_id), EL_STR("\",\"to_id\":\"")), to_id), EL_STR("\",\"relation\":\"")), relation), EL_STR("\"}"));
  return 0;
}

el_val_t route_neighbors(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t id = extract_id(path, EL_STR("/api/neighbors/"));
  if (str_eq(id, EL_STR(""))) {
    return err_json(EL_STR("missing id"));
  }
  el_val_t depth = query_int(path, EL_STR("depth"), 1);
  return engram_neighbors_json(id, depth, EL_STR("both"));
  return 0;
}

el_val_t route_strengthen(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t id = json_get_string(body, EL_STR("node_id"));
  if (str_eq(id, EL_STR(""))) {
    return err_json(EL_STR("missing node_id"));
  }
  engram_strengthen(id);
  return ok_json();
  return 0;
}

el_val_t route_forget(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t id = extract_id(path, EL_STR("/api/nodes/"));
  if (str_eq(id, EL_STR(""))) {
    return err_json(EL_STR("missing id"));
  }
  engram_forget(id);
  return ok_json();
  return 0;
}

el_val_t route_create_ise(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t content = json_get_string(body, EL_STR("content"));
  if (str_eq(content, EL_STR(""))) {
    return err_json(EL_STR("missing content"));
  }
  el_val_t sal = el_from_float(0.3);
  el_val_t imp = el_from_float(0.3);
  el_val_t conf = el_from_float(0.8);
  el_val_t id = engram_node_full(content, EL_STR("InternalStateEvent"), EL_STR("state-event"), sal, imp, conf, EL_STR("Episodic"), EL_STR("[\"internal-state\",\"InternalStateEvent\"]"));
  return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), id), EL_STR("\"}"));
  return 0;
}

el_val_t route_sync(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t dir = env(EL_STR("ENGRAM_DATA_DIR"));
  if (str_eq(dir, EL_STR(""))) {
    dir = EL_STR("/tmp/engram");
  }
  el_val_t snap_path = el_str_concat(dir, EL_STR("/sync-export.json"));
  engram_save(snap_path);
  el_val_t snap = fs_read(snap_path);
  if (str_eq(snap, EL_STR(""))) {
    return EL_STR("{\"nodes\":[],\"edges\":[]}");
  }
  return snap;
  return 0;
}

el_val_t route_save(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t p = json_get_string(body, EL_STR("path"));
  if (str_eq(p, EL_STR(""))) {
    el_val_t dir = env(EL_STR("ENGRAM_DATA_DIR"));
    if (str_eq(dir, EL_STR(""))) {
      dir = EL_STR("/tmp/engram");
    }
    p = el_str_concat(dir, EL_STR("/snapshot.json"));
  }
  engram_save(p);
  return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"path\":\""), p), EL_STR("\"}"));
  return 0;
}

el_val_t route_load(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t p = json_get_string(body, EL_STR("path"));
  if (str_eq(p, EL_STR(""))) {
    el_val_t dir = env(EL_STR("ENGRAM_DATA_DIR"));
    if (str_eq(dir, EL_STR(""))) {
      dir = EL_STR("/tmp/engram");
    }
    p = el_str_concat(dir, EL_STR("/snapshot.json"));
  }
  engram_load(p);
  return ok_json();
  return 0;
}

el_val_t route_health(el_val_t method, el_val_t path, el_val_t body) {
  return EL_STR("{\"status\":\"ok\",\"engine\":\"engram-runtime-native\"}");
  return 0;
}

el_val_t check_auth_ok(el_val_t method, el_val_t body) {
  el_val_t key = env(EL_STR("ENGRAM_API_KEY"));
  if (str_eq(key, EL_STR(""))) {
    return 1;
  }
  if (str_eq(method, EL_STR("GET"))) {
    return 1;
  }
  el_val_t provided = json_get_string(body, EL_STR("_auth"));
  if (str_eq(provided, key)) {
    return 1;
  }
  return 0;
  return 0;
}

el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t clean = strip_query(path);
  if (str_eq(method, EL_STR("GET"))) {
    if (str_eq(clean, EL_STR("/health")) || str_eq(clean, EL_STR("/"))) {
      return route_health(method, path, body);
    }
  }
  if (str_eq(method, EL_STR("POST")) && str_starts_with(clean, EL_STR("/api/neuron/state-events"))) {
    return route_create_ise(method, path, body);
  }
  if (!check_auth_ok(method, body)) {
    return err_json(EL_STR("unauthorized"));
  }
  if (str_eq(method, EL_STR("GET")) && (str_eq(clean, EL_STR("/api/stats")) || str_eq(clean, EL_STR("/stats")))) {
    return route_stats(method, path, body);
  }
  if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/nodes")) || str_eq(clean, EL_STR("/nodes")))) {
    return route_create_node(method, path, body);
  }
  if (str_eq(method, EL_STR("GET")) && (((str_eq(clean, EL_STR("/api/nodes")) || str_eq(clean, EL_STR("/nodes"))) || str_eq(clean, EL_STR("/nodes/list"))) || str_eq(clean, EL_STR("/api/nodes/list")))) {
    return route_scan_nodes(method, path, body);
  }
  if (str_eq(method, EL_STR("GET")) && (str_eq(clean, EL_STR("/api/edges")) || str_eq(clean, EL_STR("/edges")))) {
    return route_scan_edges(method, path, body);
  }
  if (str_eq(method, EL_STR("GET")) && str_starts_with(clean, EL_STR("/api/nodes/"))) {
    return route_get_node(method, path, body);
  }
  if (str_eq(method, EL_STR("DELETE")) && str_starts_with(clean, EL_STR("/api/nodes/"))) {
    return route_forget(method, path, body);
  }
  if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/edges")) || str_eq(clean, EL_STR("/edges")))) {
    return route_create_edge(method, path, body);
  }
  if (str_eq(method, EL_STR("GET")) && str_starts_with(clean, EL_STR("/api/neighbors/"))) {
    return route_neighbors(method, path, body);
  }
  if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/activate")) || str_eq(clean, EL_STR("/activate")))) {
    return route_activate(method, path, body);
  }
  if (str_eq(method, EL_STR("GET")) && str_starts_with(clean, EL_STR("/api/activate"))) {
    return route_activate(method, path, body);
  }
  if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/search")) || str_eq(clean, EL_STR("/search")))) {
    return route_search(method, path, body);
  }
  if (str_eq(method, EL_STR("GET")) && str_starts_with(clean, EL_STR("/api/search"))) {
    return route_search(method, path, body);
  }
  if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/strengthen")) || str_eq(clean, EL_STR("/strengthen")))) {
    return route_strengthen(method, path, body);
  }
  if (str_eq(method, EL_STR("GET")) && (str_eq(clean, EL_STR("/api/sync")) || str_eq(clean, EL_STR("/sync")))) {
    return route_sync(method, path, body);
  }
  if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/save")) || str_eq(clean, EL_STR("/save")))) {
    return route_save(method, path, body);
  }
  if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/load")) || str_eq(clean, EL_STR("/load")))) {
    return route_load(method, path, body);
  }
  return el_str_concat(el_str_concat(EL_STR("{\"error\":\"not found\",\"path\":\""), clean), EL_STR("\"}"));
  return 0;
}

int main(int _argc, char** _argv) {
  el_runtime_init_args(_argc, _argv);
  bind_str = env(EL_STR("ENGRAM_BIND"));
  if (str_eq(bind_str, EL_STR(""))) {
    bind_str = EL_STR(":8742");
  }
  port = parse_port(bind_str);
  data_dir = env(EL_STR("ENGRAM_DATA_DIR"));
  if (str_eq(data_dir, EL_STR(""))) {
    data_dir = EL_STR("/tmp/engram");
  }
  snapshot_path = el_str_concat(data_dir, EL_STR("/snapshot.json"));
  engram_load(snapshot_path);
  println(EL_STR("[engram] runtime-native graph engine"));
  println(el_str_concat(EL_STR("[engram] data_dir="), data_dir));
  println(el_str_concat(EL_STR("[engram] node_count="), int_to_str(engram_node_count())));
  println(el_str_concat(EL_STR("[engram] edge_count="), int_to_str(engram_edge_count())));
  println(el_str_concat(EL_STR("[engram] listening on "), int_to_str(port)));
  http_set_handler(EL_STR("handle_request"));
  http_serve(port, EL_STR("handle_request"));
  return 0;
}

