// Minimal bootstrap — seeds the content-addressed cache
// Only runs on cold start (C runtime couldn't find engine in cache)
// Hidden vars: os, core_path, shop_path, native_mode (optional)
var load_internal = os.load_internal
function use_embed(name) {
  return load_internal("js_core_" + name + "_use")
}

var fd = use_embed('internal_fd')
var json_mod = use_embed('json')
var crypto = use_embed('internal_crypto')

function content_hash(content) {
  var data = content
  if (!is_blob(data)) data = stone(blob(text(data)))
  return text(crypto.blake2(data), 'h')
}

function cache_path(hash) {
  if (!shop_path) return null
  return shop_path + '/build/' + hash
}

function ensure_build_dir() {
  if (!shop_path) return null
  var dir = shop_path + '/build'
  if (!fd.is_dir(dir)) fd.mkdir(dir)
  return dir
}

// Load seed pipeline from boot/
function boot_load(name) {
  var mcode_path = core_path + '/boot/' + name + '.cm.mcode'
  var mcode_blob = null
  var mach_blob = null
  if (!fd.is_file(mcode_path)) {
    os.print("error: missing seed: " + name + "\n")
    disrupt
  }
  mcode_blob = fd.slurp(mcode_path)
  mach_blob = mach_compile_mcode_bin(name, text(mcode_blob))
  return mach_load(mach_blob, stone({use: use_embed}))
}

var tokenize_mod = boot_load("tokenize")
var parse_mod = boot_load("parse")
var fold_mod = boot_load("fold")
var mcode_mod = boot_load("mcode")
var streamline_mod = boot_load("streamline")

function analyze(src, filename) {
  var tok_result = tokenize_mod(src, filename)
  var ast = parse_mod(tok_result.tokens, src, filename, tokenize_mod)
  var _i = 0
  var e = null
  var msg = null
  var has_errors = ast.errors != null && length(ast.errors) > 0
  if (has_errors) {
    while (_i < length(ast.errors)) {
      e = ast.errors[_i]
      msg = e.message
      if (e.line != null && e.column != null)
        os.print(`${filename}:${text(e.line)}:${text(e.column)}: error: ${msg}\n`)
      else
        os.print(`${filename}: error: ${msg}\n`)
      _i = _i + 1
    }
    disrupt
  }
  return fold_mod(ast)
}

function compile_and_cache(name, source_path) {
  var source_blob = fd.slurp(source_path)
  var hash = content_hash(source_blob)
  var cached = cache_path(hash)
  var ast = null
  var compiled = null
  var mcode_json = null
  var mach_blob = null
  if (cached && fd.is_file(cached)) return
  ast = analyze(text(source_blob), source_path)
  compiled = streamline_mod(mcode_mod(ast))
  mcode_json = json_mod.encode(compiled)
  mach_blob = mach_compile_mcode_bin(name, mcode_json)
  if (cached) {
    ensure_build_dir()
    fd.slurpwrite(cached, mach_blob)
  }
}

// --- Native compilation support ---

function detect_host_target() {
  var platform = os.platform()
  var arch = os.arch ? os.arch() : 'arm64'
  if (platform == 'macOS' || platform == 'darwin')
    return arch == 'x86_64' ? 'macos_x86_64' : 'macos_arm64'
  if (platform == 'Linux' || platform == 'linux')
    return arch == 'x86_64' ? 'linux' : 'linux_arm64'
  if (platform == 'Windows' || platform == 'windows')
    return 'windows'
  return null
}

function detect_cc() {
  var platform = os.platform()
  if (platform == 'macOS') return 'clang'
  return 'cc'
}

// Compute native dylib cache path matching build.cm's scheme:
// cache_path(native_cache_content(src, target, ''), SALT_NATIVE)
function native_dylib_cache_path(src, target) {
  var native_key = src + '\n' + target + '\nnative\n'
  var full_key = native_key + '\nnative'
  return cache_path(content_hash(full_key))
}

// Compile a module to a native dylib and cache it
var _qbe_mod = null
var _qbe_emit_mod = null
var _host_target = null
var _cc = null
var _is_darwin = false
var _rt_compiled = false

function compile_native_cached(name, source_path) {
  var source_blob = fd.slurp(source_path)
  var src = text(source_blob)
  var dylib_path = native_dylib_cache_path(src, _host_target)
  var ast = null
  var compiled = null
  var il_parts = null
  var helpers_il = null
  var all_fns = null
  var full_il = null
  var asm_text = null
  var tmp = null
  var rc = null
  var rt_o = null
  var qbe_rt_path = null
  var link_cmd = null

  if (dylib_path && fd.is_file(dylib_path)) {
    os.print("bootstrap: native cache hit: " + name + "\n")
    return
  }

  os.print("bootstrap: compiling native: " + name + "\n")
  ast = analyze(src, source_path)
  compiled = streamline_mod(mcode_mod(ast))
  il_parts = _qbe_emit_mod(compiled, _qbe_mod, null)

  helpers_il = (il_parts.helpers && length(il_parts.helpers) > 0)
    ? text(il_parts.helpers, "\n") : ""
  all_fns = text(il_parts.functions, "\n")
  full_il = il_parts.data + "\n\n" + helpers_il + "\n\n" + all_fns

  asm_text = os.qbe(full_il)
  tmp = '/tmp/cell_boot_' + name
  fd.slurpwrite(tmp + '.s', stone(blob(asm_text)))

  rc = os.system(_cc + ' -c ' + tmp + '.s -o ' + tmp + '.o')
  if (rc != 0) {
    os.print("error: assembly failed for " + name + "\n")
    disrupt
  }

  // Compile QBE runtime stubs (once)
  rt_o = '/tmp/cell_qbe_rt.o'
  if (!_rt_compiled && !fd.is_file(rt_o)) {
    qbe_rt_path = core_path + '/src/qbe_rt.c'
    rc = os.system(_cc + ' -c ' + qbe_rt_path + ' -o ' + rt_o + ' -fPIC')
    if (rc != 0) {
      os.print("error: qbe_rt compilation failed\n")
      disrupt
    }
    _rt_compiled = true
  }

  // Link dylib
  ensure_build_dir()
  link_cmd = _cc + ' -shared -fPIC'
  if (_is_darwin)
    link_cmd = link_cmd + ' -undefined dynamic_lookup'
  link_cmd = link_cmd + ' ' + tmp + '.o ' + rt_o + ' -o ' + dylib_path

  rc = os.system(link_cmd)
  if (rc != 0) {
    os.print("error: linking failed for " + name + "\n")
    disrupt
  }
}

// --- Main bootstrap logic ---

// Check if native_mode was passed from C runtime
var _native = false
var _check_nm = function() {
  if (native_mode) _native = true
} disruption {}
_check_nm()

var _targets = null
var _ti = 0
var _te = null

if (_native) {
  // Native path: compile everything to native dylibs
  _qbe_mod = boot_load("qbe")
  _qbe_emit_mod = boot_load("qbe_emit")
  _host_target = detect_host_target()
  _cc = detect_cc()
  _is_darwin = os.platform() == 'macOS'

  if (!_host_target) {
    os.print("error: could not detect host target for native compilation\n")
    disrupt
  }

  // Also seed bytecode cache for engine (so non-native path still works)
  compile_and_cache("engine", core_path + '/internal/engine.cm')

  // Compile pipeline modules + qbe/qbe_emit + engine to native dylibs
  _targets = [
    {name: "tokenize", path: "tokenize.cm"},
    {name: "parse", path: "parse.cm"},
    {name: "fold", path: "fold.cm"},
    {name: "mcode", path: "mcode.cm"},
    {name: "streamline", path: "streamline.cm"},
    {name: "qbe", path: "qbe.cm"},
    {name: "qbe_emit", path: "qbe_emit.cm"},
    {name: "engine", path: "internal/engine.cm"}
  ]
  _ti = 0
  while (_ti < length(_targets)) {
    _te = _targets[_ti]
    compile_native_cached(_te.name, core_path + '/' + _te.path)
    _ti = _ti + 1
  }
  os.print("bootstrap: native cache seeded\n")
} else {
  // Bytecode path: seed cache with everything engine needs
  _targets = [
    {name: "tokenize", path: "tokenize.cm"},
    {name: "parse", path: "parse.cm"},
    {name: "fold", path: "fold.cm"},
    {name: "mcode", path: "mcode.cm"},
    {name: "streamline", path: "streamline.cm"},
    {name: "engine", path: "internal/engine.cm"}
  ]
  _ti = 0
  while (_ti < length(_targets)) {
    _te = _targets[_ti]
    compile_and_cache(_te.name, core_path + '/' + _te.path)
    _ti = _ti + 1
  }
  os.print("bootstrap: cache seeded\n")
}
