// stage1_cli.hako — Stage‑1 self-host CLI skeleton（骨組みのみ）
//
// 役割: Stage‑B → Stage‑1 UsingResolver → MirBuilder を経由する self-host CLI の
//      入口シグネチャを固定するだけの箱。Phase 25.1A-3 で最低限の中身を実装。
// トグル（既定OFF）:
//   - NYASH_USE_STAGE1_CLI=1             : Rust 側がこの CLI を呼ぶときに使用
//   - STAGE1_EMIT_PROGRAM_JSON=1         : source → Program(JSON v0) を emit
//   - STAGE1_EMIT_MIR_JSON=1             : Program(JSON v0) → MIR(JSON) を emit
// 環境変数（env-only 仕様）:
//   - STAGE1_SOURCE        : .hako ソースパス（FileBox 経由で読み込む）
//   - STAGE1_SOURCE_TEXT   : ソース文字列（FileBox なしで渡すテスト用）
//   - STAGE1_PROGRAM_JSON  : 事前生成した Program(JSON v0) のパス（emit_mir_json/run で利用）
//   - STAGE1_BACKEND       : backend 選択（vm/llvm/pyvm、既定 vm）
//   - STAGE1_CLI_DEBUG     : debug ログを有効化

using lang.compiler.build.build_box as BuildBox
using lang.compiler.entry.using_resolver_box as Stage1UsingResolverBox
using lang.mir.builder.MirBuilderBox
using selfhost.shared.host_bridge.codegen_bridge as CodegenBridgeBox

// Stage-1 CLI Config Box - Environment variable parsing and configuration management
static box Stage1CliConfigBox {
  // Parse all env vars into a Config map (MapBox)
  method from_env() {
    local cfg = new MapBox()

    // Mode selection (mutually exclusive)
    if env.get("STAGE1_EMIT_PROGRAM_JSON") == "1" {
      cfg.set("mode", "emit_program_json")
    } else if env.get("STAGE1_EMIT_MIR_JSON") == "1" {
      cfg.set("mode", "emit_mir_json")
    } else if env.get("NYASH_USE_STAGE1_CLI") == "1" {
      cfg.set("mode", "run")
    } else {
      cfg.set("mode", "disabled")
    }

    // Backend (default: "vm")
    local b = env.get("STAGE1_BACKEND")
    if b == null { b = "vm" }
    cfg.set("backend", "" + b)

    // Input sources
    cfg.set("source_path", env.get("STAGE1_SOURCE"))
    cfg.set("source_text", env.get("STAGE1_SOURCE_TEXT"))
    cfg.set("program_json_path", env.get("STAGE1_PROGRAM_JSON"))

    // Dev flags
    cfg.set("debug", env.get("STAGE1_CLI_DEBUG"))
    cfg.set("to_i64_force_zero", env.get("NYASH_TO_I64_FORCE_ZERO"))

    return cfg
  }
}

static box Stage1Cli {
  // Source(.hako) → Program(JSON v0)
  method emit_program_json(source) {
    local tag = "[stage1-cli] emit program-json"
    print("[stage1-cli/debug] emit_program_json ENTRY, source=" + ("" + source))
    local src = env.get("STAGE1_SOURCE_TEXT")
    if src == null {
      if source == null || ("" + source) == "" {
        print(tag + ": source path is required")
        return null
      }
      src = me._read_file(tag, "" + source)
      if env.get("STAGE1_CLI_DEBUG") == "1" {
        local status = "null"
        if src != null { status = "non-null (length=" + ("" + src.length()) + ")" }
        print("[stage1-cli/debug] emit_program_json: _read_file returned, src=" + status)
      }
    } else if env.get("STAGE1_CLI_DEBUG") == "1" {
      print("[stage1-cli/debug] emit_program_json: STAGE1_SOURCE_TEXT provided (length=" + ("" + src.length()) + ")")
    }
    if src == null { return null }

    // Stage‑1 UsingResolver: prefix concat is no-op when HAKO_STAGEB_APPLY_USINGS=0
    local prefix = Stage1UsingResolverBox.resolve_for_source(src)
    if prefix == null { prefix = "" }
    local merged = prefix + src
    print("[stage1-cli/debug] emit_program_json: calling BuildBox.emit_program_json_v0")

    local prog = BuildBox.emit_program_json_v0(merged, null)
    {
      local status = "null"
      if prog != null { status = "non-null" }
      print("[stage1-cli/debug] emit_program_json: BuildBox.emit_program_json_v0 returned, prog=" + status)
    }
    if prog == null {
      print(tag + ": BuildBox returned null")
      return null
    }
    local ps = "" + prog
    print("[stage1-cli/debug] emit_program_json: stringified prog, length=" + ("" + ps.length()))
    if ps.indexOf("\"version\":0") < 0 || ps.indexOf("\"kind\":\"Program\"") < 0 {
      print(tag + ": unexpected Program(JSON) output (missing version/kind)")
      return null
    }
    print("[stage1-cli/debug] emit_program_json: validation passed, returning Program JSON")
    return ps
  }

  // Program(JSON v0) → MIR(JSON)
  method emit_mir_json(program_json) {
    local tag = "[stage1-cli] emit mir-json"
    if program_json == null {
      print(tag + ": program_json is null")
      return null
    }
    local mir = MirBuilderBox.emit_from_program_json_v0(program_json, null)
    if mir == null {
      print(tag + ": MirBuilderBox returned null (enable HAKO_MIR_BUILDER_DELEGATE=1 ?)")
      return null
    }
    return "" + mir
  }

  // Run Program(JSON v0) on selected backend (vm|llvm|pyvm)
  // - vm/pyvm: convert to MIR(JSON) and print to stdout（実行 path は未配線）
  // - llvm   : emit object via env.codegen; TODO: link+exec in後続フェーズ
  method run_program_json(program_json, backend) {
    local tag = "[stage1-cli] run program-json"
    if program_json == null {
      print(tag + ": program_json is null")
      return 98
    }
    if backend == null { backend = "vm" }
    backend = "" + backend

    local mir = me.emit_mir_json(program_json)
    if mir == null { return 98 }

    if backend == "llvm" {
      local obj = CodegenBridgeBox.emit_object(mir)
      if obj == null || ("" + obj) == "" {
        print(tag + ": env.codegen.emit_object failed")
        return 98
      }
      print("[stage1-cli] llvm object: " + ("" + obj))
      return 0
    }

    // vm / pyvm: 現状は MIR(JSON) を吐いて終了（実行は Stage0 側に後続で橋渡し）
    print(mir)
    return 0
  }

  // CLI dispatcher (Config box refactored)
  method stage1_main(cli_args_raw) {
    // Fail-fast: Work around MIR Builder type confusion by avoiding parameter usage
    // The parameter cli_args_raw is mistyped as ParserBox, so we ignore it entirely
    // and create a fresh ArrayBox from environment variables instead
    local args_safe = new ArrayBox()

    // 現状: MIR 型レジストリの制約により MapBox ベースの ConfigBox は本線では使用しない。
    // env から直接読み取り、Stage1CliConfigBox は将来の構造化用として温存する。

    // mode 判定（env-only）
    // 新形式（NYASH_STAGE1_MODE）があればそちらを優先し、旧 STAGE1_* はフォールバックとして扱う。
    local mode = env.get("NYASH_STAGE1_MODE")
    if mode != null { mode = "" + mode }
    if mode == null || mode == "" {
      if env.get("STAGE1_EMIT_PROGRAM_JSON") == "1" {
        mode = "emit-program"
      } else if env.get("STAGE1_EMIT_MIR_JSON") == "1" {
        mode = "emit-mir"
      } else if env.get("NYASH_USE_STAGE1_CLI") == "1" {
        mode = "run"
      } else {
        mode = "disabled"
      }
    }

    // backend と入力まわり
    local backend = env.get("NYASH_STAGE1_BACKEND")
    if backend == null || backend == "" {
      backend = env.get("STAGE1_BACKEND")
    }
    if backend == null { backend = "vm" }
    backend = "" + backend

    local source = env.get("NYASH_STAGE1_INPUT")
    if source == null || source == "" {
      source = env.get("STAGE1_SOURCE")
    }
    local source_text = env.get("STAGE1_SOURCE_TEXT")
    local prog_path = env.get("NYASH_STAGE1_PROGRAM_JSON")
    if prog_path == null || prog_path == "" {
      prog_path = env.get("STAGE1_PROGRAM_JSON")
    }

    // デバッグは当面 STAGE1_CLI_DEBUG を優先し、将来 NYASH_DEBUG に統合する。
    local debug = env.get("STAGE1_CLI_DEBUG")

    // Log entry point for debugging
    if debug == "1" {
      __mir__.log("[stage1_main] args_safe at entry", args_safe)
      print("[stage1-cli/debug] stage1_main ENTRY: mode=" + ("" + mode) + " backend=" + backend)
      __mir__.log("[stage1_main] env-config",
        mode,
        backend,
        source,
        prog_path)
    }

    // Early exit if CLI is disabled
    if mode == "disabled" {
      if debug == "1" {
        print("[stage1-cli/debug] stage1_main: mode=disabled, returning 97")
      }
      return 97
    }

    // Dispatch by mode
    if mode == "emit-program" || mode == "emit_program_json" {
      if source == null || source == "" {
        if source_text != null && source_text != "" {
          source = source_text  // Fallback to direct text for testing
        } else {
          print("[stage1-cli] emit program-json: STAGE1_SOURCE is required")
          return 96
        }
      }
      local ps = me.emit_program_json(source)
      if ps == null { return 96 }
      print(ps)
      return 0
    }

    if mode == "emit-mir" || mode == "emit_mir_json" {
      local prog_json = null
      if prog_path != null && prog_path != "" {
        prog_json = me._read_file("[stage1-cli] emit mir-json", prog_path)
      } else {
        if source == null || source == "" {
          print("[stage1-cli] emit mir-json: STAGE1_SOURCE or STAGE1_PROGRAM_JSON is required")
          return 96
        }
        prog_json = me.emit_program_json(source)
      }
      if prog_json == null { return 96 }
      // Rust parent lowers Program(JSON v0) → MIR (json_v0_bridge) to keep dump flags共通。
      print(prog_json)
      return 0
    }

    // Default: run path (mode == "run")
    local prog_json = null
    if prog_path != null && prog_path != "" {
      prog_json = me._read_file("[stage1-cli] run", prog_path)
    } else {
      if source == null || source == "" {
        if source_text != null && source_text != "" {
          source = source_text
        } else {
          print("[stage1-cli] run: source path is required (set STAGE1_SOURCE)")
          return 96
        }
      }
      prog_json = me.emit_program_json(source)
    }
    if prog_json == null { return 96 }
    return me.run_program_json(prog_json, backend)
  }

  // hakorune emit {program-json|mir-json} ...
  method _cmd_emit(args){
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      __mir__.log("[_cmd_emit] args before size check", args)
    }
    local argc = 0; if args != null { argc = args.size() }
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      __mir__.log("[_cmd_emit] argc after size", argc)
      print("[stage1-cli/debug] _cmd_emit: argc=" + ("" + argc))
    }
    if argc < 2 {
      print("[stage1-cli] emit: usage: emit program-json <source.hako> | emit mir-json [--from-program-json <file>] <source.hako>")
      return 96
    }
    local sub = "" + args.get(1)
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      print("[stage1-cli/debug] _cmd_emit: sub=" + sub)
    }
    if sub == "program-json" { return me._cmd_emit_program_json(args) }
    if sub == "mir-json" { return me._cmd_emit_mir_json(args) }
    print("[stage1-cli] emit: unknown subcommand: " + sub)
    return 96
  }

  method _cmd_emit_program_json(args){
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      __mir__.log("[_cmd_emit_program_json] args before size check", args)
    }
    local argc = 0; if args != null { argc = args.size() }
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      __mir__.log("[_cmd_emit_program_json] argc after size", argc)
      print("[stage1-cli/debug] _cmd_emit_program_json: argc=" + ("" + argc))
    }
    if argc < 3 {
      print("[stage1-cli] emit program-json: usage: emit program-json <source.hako>")
      return 96
    }
    local path = "" + args.get(2)
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      print("[stage1-cli/debug] _cmd_emit_program_json: calling emit_program_json with path=" + path)
    }
    local ps = me.emit_program_json(path)
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      local status = "null"
      if ps != null { status = "non-null" }
      print("[stage1-cli/debug] _cmd_emit_program_json: emit_program_json returned, ps=" + status)
    }
    if ps == null { return 96 }
    print(ps)
    return 0
  }

  method _cmd_emit_mir_json(args){
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      __mir__.log("[_cmd_emit_mir_json] args before size check", args)
    }
    local argc = 0; if args != null { argc = args.size() }
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      __mir__.log("[_cmd_emit_mir_json] argc after size", argc)
      print("[stage1-cli/debug] _cmd_emit_mir_json: argc=" + ("" + argc))
    }
    local prog_path = null
    local source_path = null
    // Predeclare to avoid creating loop-body-local variables in the condition scope.
    local arg = ""

    local i = 2
    loop(i < argc) {
      arg = "" + args.get(i)
      if arg == "--from-program-json" {
        if i + 1 >= argc {
          print("[stage1-cli] emit mir-json: --from-program-json requires a path")
          return 96
        }
        prog_path = "" + args.get(i + 1)
        i = i + 2
      } else {
        if source_path == null {
          source_path = arg
          i = i + 1
        } else {
          print("[stage1-cli] emit mir-json: unexpected extra argument: " + arg)
          return 96
        }
      }
    }

    if env.get("STAGE1_CLI_DEBUG") == "1" {
      print("[stage1-cli/debug] _cmd_emit_mir_json: source_path=" + source_path + " prog_path=" + prog_path)
    }

    if prog_path != null && prog_path != "" && source_path != null && source_path != "" {
      print("[stage1-cli] emit mir-json: specify either --from-program-json or <source.hako>")
      return 96
    }
    if (prog_path == null || prog_path == "") && (source_path == null || source_path == "") {
      print("[stage1-cli] emit mir-json: require --from-program-json <file> or <source.hako>")
      return 96
    }

    local prog_json = null
    if prog_path != null && prog_path != "" {
      prog_json = me._read_file("[stage1-cli] emit mir-json", prog_path)
    } else {
      prog_json = me.emit_program_json(source_path)
    }
    if prog_json == null { return 96 }

    local mir = me.emit_mir_json(prog_json)
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      local status = "null"
      if mir != null { status = "non-null" }
      print("[stage1-cli/debug] _cmd_emit_mir_json: emit_mir_json returned, mir=" + status)
    }
    if mir == null { return 96 }
    print(mir)
    return 0
  }

  // hakorune run --backend <b> <source.hako> [-- args...]
  method _cmd_run(args){
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      __mir__.log("[_cmd_run] args before size check", args)
    }
    local argc = 0; if args != null { argc = args.size() }
    if env.get("STAGE1_CLI_DEBUG") == "1" {
      __mir__.log("[_cmd_run] argc after size", argc)
    }
    local backend = "vm"
    local source_path = null
    // Predeclare to keep the loop condition independent of loop-body-local variables.
    local arg = ""

    local i = 1
    loop(i < argc) {
      arg = "" + args.get(i)
      if arg == "--backend" {
        if i + 1 >= argc {
          print("[stage1-cli] run: --backend requires a value (vm|llvm|pyvm)")
          return 96
        }
        backend = "" + args.get(i + 1)
        i = i + 2
        continue
      }

      // First non-option argument is the source path; exit loop after capture.
      source_path = arg
      i = i + 1
      if source_path != null { i = argc }
    }

    if source_path == null || source_path == "" {
      print("[stage1-cli] run: source path is required")
      return 96
    }

    // Remaining args after source are preserved as NYASH_SCRIPT_ARGS_JSON when unset
    {
      local extras = new ArrayBox()
      loop(i < argc) {
        extras.push("" + args.get(i))
        i = i + 1
      }
      if extras.length() > 0 {
        // Build a minimal JSON array string; avoid env.set unless already available
        local json = "["
        local j = 0; loop(j < extras.length()) {
          if j > 0 { json = json + "," }
          local s = extras.get(j)
          json = json + "\"" + me._escape_json_string("" + s) + "\""
          j = j + 1
        }
        json = json + "]"
        // env.set may be unavailable on some runtimes; best-effort
        env.set("NYASH_SCRIPT_ARGS_JSON", json)
      }
    }

    local prog_json = me.emit_program_json(source_path)
    if prog_json == null { return 96 }
    return me.run_program_json(prog_json, backend)
  }

  method _read_file(tag, path) {
    if path == null || path == "" {
      print(tag + ": source path is required")
      return null
    }
    local fb = new FileBox()
    local ok = fb.open(path, "r")
    if ok != 1 {
      print(tag + ": failed to open " + path)
      return null
    }
    local content = fb.read()
    fb.close()
    if content == null || content == "" {
      print(tag + ": source is empty")
      return null
    }
    return "" + content
  }

  method _escape_json_string(s) {
    local out = ""
    if s == null { return out }
    local i = 0
    local n = s.length()
    loop(i < n) {
      local ch = s.substring(i, i + 1)
      if ch == "\\" { out = out + "\\\\" }
      else if ch == "\"" { out = out + "\\\"" }
      else if ch == "\n" { out = out + "\\n" }
      else if ch == "\r" { out = out + "\\r" }
      else if ch == "\t" { out = out + "\\t" }
      else { out = out + ch }
      i = i + 1
    }
    return out
  }
}

static box Stage1CliMain {
  // Entry point used by the Rust bridge. Keep arity 0 to avoid undefined args.
  // Stage1Cli.stage1_main は env-only 仕様なのでここで空配列を渡す。
  main() {
    local cli = new Stage1Cli()
    local empty = new ArrayBox()
    return cli.stage1_main(empty)
  }
}
