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

el_val_t pkey(el_val_t name, el_val_t field);
el_val_t put_str(el_val_t name, el_val_t field, el_val_t value);
el_val_t put_bool(el_val_t name, el_val_t field, el_val_t value);
el_val_t get_str(el_val_t name, el_val_t field);
el_val_t get_bool(el_val_t name, el_val_t field);
el_val_t index_list(void);
el_val_t index_set(el_val_t names);
el_val_t index_contains(el_val_t name);
el_val_t index_add(el_val_t name);
el_val_t register_plugin(el_val_t name, el_val_t version, el_val_t description, el_val_t installed, el_val_t enabled, el_val_t hooks, el_val_t first_party);
el_val_t plugin_init(void);
el_val_t json_escape_str(el_val_t s);
el_val_t quote_json(el_val_t s);
el_val_t hooks_to_json(el_val_t hooks_csv);
el_val_t plugin_to_json(el_val_t name);
el_val_t plugin_list_json(void);
el_val_t plugin_get_json(el_val_t name);
el_val_t plugin_install(el_val_t name);
el_val_t plugin_remove(el_val_t name);
el_val_t plugin_enable(el_val_t name);
el_val_t plugin_disable(el_val_t name);
el_val_t host_main(void);

el_val_t pkey(el_val_t name, el_val_t field) {
  return el_str_concat(el_str_concat(el_str_concat(EL_STR("plugin/"), name), EL_STR("/")), field);
  return 0;
}

el_val_t put_str(el_val_t name, el_val_t field, el_val_t value) {
  state_set(pkey(name, field), value);
  return 0;
}

el_val_t put_bool(el_val_t name, el_val_t field, el_val_t value) {
  if (value) {
    state_set(pkey(name, field), EL_STR("true"));
  }
  if (!value) {
    state_set(pkey(name, field), EL_STR("false"));
  }
  return 0;
}

el_val_t get_str(el_val_t name, el_val_t field) {
  return state_get(pkey(name, field));
  return 0;
}

el_val_t get_bool(el_val_t name, el_val_t field) {
  return str_eq(state_get(pkey(name, field)), EL_STR("true"));
  return 0;
}

el_val_t index_list(void) {
  el_val_t raw = state_get(EL_STR("plugins/index"));
  if (str_eq(raw, EL_STR(""))) {
    return native_list_empty();
  }
  return str_split(raw, EL_STR(","));
  return 0;
}

el_val_t index_set(el_val_t names) {
  state_set(EL_STR("plugins/index"), list_join(names, EL_STR(",")));
  return 0;
}

el_val_t index_contains(el_val_t name) {
  el_val_t names = index_list();
  el_val_t n = native_list_len(names);
  el_val_t i = 0;
  while (i < n) {
    if (str_eq(native_list_get(names, i), name)) {
      return 1;
    }
    i = (i + 1);
  }
  return 0;
  return 0;
}

el_val_t index_add(el_val_t name) {
  if (!index_contains(name)) {
    el_val_t names = index_list();
    names = native_list_append(names, name);
    index_set(names);
  }
  return 0;
}

el_val_t register_plugin(el_val_t name, el_val_t version, el_val_t description, el_val_t installed, el_val_t enabled, el_val_t hooks, el_val_t first_party) {
  put_str(name, EL_STR("name"), name);
  put_str(name, EL_STR("version"), version);
  put_str(name, EL_STR("description"), description);
  put_bool(name, EL_STR("installed"), installed);
  put_bool(name, EL_STR("enabled"), enabled);
  put_str(name, EL_STR("hooks"), hooks);
  put_bool(name, EL_STR("first_party"), first_party);
  index_add(name);
  return 0;
}

el_val_t plugin_init(void) {
  if (str_eq(state_get(EL_STR("plugins/initialised")), EL_STR("true"))) {
    return 0;
  }
  register_plugin(EL_STR("el-theme-dark"), EL_STR("1.0.0"), EL_STR("Dark theme — the default Engram IDE theme."), 1, 1, EL_STR(""), 1);
  register_plugin(EL_STR("el-theme-light"), EL_STR("1.0.0"), EL_STR("Light theme for high-ambient-light environments."), 0, 0, EL_STR(""), 1);
  register_plugin(EL_STR("el-fmt"), EL_STR("0.1.0"), EL_STR("Code formatter — auto-formats .el files on save."), 1, 1, EL_STR("on_save"), 1);
  register_plugin(EL_STR("el-doc"), EL_STR("0.1.0"), EL_STR("Documentation generator — produces HTML docs from type definitions."), 0, 0, EL_STR("custom_tool"), 1);
  register_plugin(EL_STR("el-test"), EL_STR("0.1.0"), EL_STR("Test runner with inline results displayed in the editor gutter."), 0, 0, EL_STR("on_build_complete,custom_panel"), 1);
  state_set(EL_STR("plugins/initialised"), EL_STR("true"));
  return 0;
}

el_val_t json_escape_str(el_val_t s) {
  el_val_t chars = native_string_chars(s);
  el_val_t n = native_list_len(chars);
  el_val_t out = EL_STR("");
  el_val_t i = 0;
  while (i < n) {
    el_val_t ch = native_list_get(chars, i);
    if (str_eq(ch, EL_STR("\""))) {
      out = el_str_concat(out, EL_STR("\\\""));
    } else {
      if (str_eq(ch, EL_STR("\\"))) {
        out = el_str_concat(out, EL_STR("\\\\"));
      } else {
        if (str_eq(ch, EL_STR("\n"))) {
          out = el_str_concat(out, EL_STR("\\n"));
        } else {
          out = el_str_concat(out, ch);
        }
      }
    }
    i = (i + 1);
  }
  return out;
  return 0;
}

el_val_t quote_json(el_val_t s) {
  return el_str_concat(el_str_concat(EL_STR("\""), json_escape_str(s)), EL_STR("\""));
  return 0;
}

el_val_t hooks_to_json(el_val_t hooks_csv) {
  if (str_eq(hooks_csv, EL_STR(""))) {
    return EL_STR("[]");
  }
  el_val_t xs = str_split(hooks_csv, EL_STR(","));
  el_val_t n = native_list_len(xs);
  el_val_t out = EL_STR("[");
  el_val_t i = 0;
  while (i < n) {
    if (i > 0) {
      out = el_str_concat(out, EL_STR(","));
    }
    out = el_str_concat(out, quote_json(native_list_get(xs, i)));
    i = (i + 1);
  }
  return el_str_concat(out, EL_STR("]"));
  return 0;
}

el_val_t plugin_to_json(el_val_t name) {
  el_val_t parts = native_list_empty();
  parts = native_list_append(parts, el_str_concat(EL_STR("\"name\":"), quote_json(get_str(name, EL_STR("name")))));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"version\":"), quote_json(get_str(name, EL_STR("version")))));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"description\":"), quote_json(get_str(name, EL_STR("description")))));
  el_val_t installed = get_str(name, EL_STR("installed"));
  el_val_t enabled = get_str(name, EL_STR("enabled"));
  el_val_t first_party = get_str(name, EL_STR("first_party"));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"installed\":"), installed));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"enabled\":"), enabled));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"hooks\":"), hooks_to_json(get_str(name, EL_STR("hooks")))));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"first_party\":"), first_party));
  return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
  return 0;
}

el_val_t plugin_list_json(void) {
  plugin_init();
  el_val_t names = index_list();
  el_val_t n = native_list_len(names);
  el_val_t out = EL_STR("[");
  el_val_t i = 0;
  while (i < n) {
    if (i > 0) {
      out = el_str_concat(out, EL_STR(","));
    }
    out = el_str_concat(out, plugin_to_json(native_list_get(names, i)));
    i = (i + 1);
  }
  return el_str_concat(out, EL_STR("]"));
  return 0;
}

el_val_t plugin_get_json(el_val_t name) {
  plugin_init();
  if (!index_contains(name)) {
    return EL_STR("null");
  }
  return plugin_to_json(name);
  return 0;
}

el_val_t plugin_install(el_val_t name) {
  plugin_init();
  if (!index_contains(name)) {
    return EL_STR("{\"error\":\"plugin registry is not available (registry URL not configured)\"}");
  }
  if (get_bool(name, EL_STR("installed"))) {
    return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' is already installed\"}"));
  }
  put_bool(name, EL_STR("installed"), 1);
  put_bool(name, EL_STR("enabled"), 1);
  return plugin_to_json(name);
  return 0;
}

el_val_t plugin_remove(el_val_t name) {
  plugin_init();
  if (!index_contains(name)) {
    return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' not found\"}"));
  }
  if (str_eq(name, EL_STR("el-theme-dark"))) {
    return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' cannot be removed: it is a required built-in\"}"));
  }
  put_bool(name, EL_STR("installed"), 0);
  put_bool(name, EL_STR("enabled"), 0);
  return EL_STR("{\"ok\":true}");
  return 0;
}

el_val_t plugin_enable(el_val_t name) {
  plugin_init();
  if (!index_contains(name)) {
    return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' not found\"}"));
  }
  put_bool(name, EL_STR("enabled"), 1);
  return EL_STR("{\"ok\":true}");
  return 0;
}

el_val_t plugin_disable(el_val_t name) {
  plugin_init();
  if (!index_contains(name)) {
    return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' not found\"}"));
  }
  put_bool(name, EL_STR("enabled"), 0);
  return EL_STR("{\"ok\":true}");
  return 0;
}

el_val_t host_main(void) {
  el_val_t argv = args();
  el_val_t n = native_list_len(argv);
  if (n == 0) {
    println(plugin_list_json());
    return 0;
  }
  el_val_t cmd = native_list_get(argv, 0);
  if (str_eq(cmd, EL_STR("list"))) {
    println(plugin_list_json());
    return 0;
  }
  if (str_eq(cmd, EL_STR("get"))) {
    if (n >= 2) {
      println(plugin_get_json(native_list_get(argv, 1)));
    }
    return 0;
  }
  if (str_eq(cmd, EL_STR("install"))) {
    if (n >= 2) {
      println(plugin_install(native_list_get(argv, 1)));
    }
    return 0;
  }
  if (str_eq(cmd, EL_STR("remove"))) {
    if (n >= 2) {
      println(plugin_remove(native_list_get(argv, 1)));
    }
    return 0;
  }
  if (str_eq(cmd, EL_STR("enable"))) {
    if (n >= 2) {
      println(plugin_enable(native_list_get(argv, 1)));
    }
    return 0;
  }
  if (str_eq(cmd, EL_STR("disable"))) {
    if (n >= 2) {
      println(plugin_disable(native_list_get(argv, 1)));
    }
    return 0;
  }
  println(EL_STR("usage: el-plugin-host [list|get|install|remove|enable|disable] <name>"));
  return 0;
}

int main(int argc, char** argv) {
  el_runtime_init_args(argc, argv);
  host_main();
  return 0;
}

