// UsingResolveSSOTBox — SSOT for using/modules resolution (Phase 22.1)
// Contract (MVP):
// - Pure function. IO（filesystem access）禁止。与えられた ctx のみを参照。
// - 優先順: modules（nyash.toml 明示） > relative 推定（cwd → using_paths の順） > 見つからない（null）
// - relative 推定は ctx.relative_hint=="1" が有効時のみ（既定OFF／挙動不変）。
// - 曖昧（複数候補）の最終判断は Runner 側。strict=1 時は legacy へ委譲する（本箱は null を返すのが安全）。
// Extension points:
// - ctx.modules: Map<String,String>（厳密一致）
// - ctx.using_paths: Array<String>（将来のヒント／本箱では純粋合成のみ）
// - ctx.cwd: String（相対の基準）

// Quick README (Phase 25.1 using設計)
// - 役割: using 解決の唯一の窓口（SSOT）。parser/Stage‑B/Stage‑1 は IO や名前探索をここに委譲する。
// - I/F:
//   - resolve(name, ctx) -> path|null : modules/cwd/using_paths から純粋に合成して返す（IOなし）。
//   - resolve_modules(modules_json, using_entries_json, ctx) -> modules_json_resolved|null :
//       modules_json（nyash.toml 相当）と using_entries_json（UsingCollector 出力）を突き合わせ、
//       「解決済み modules_json」（同名重複などの調停結果）を返す。IOなし。
//   - resolve_prefix(using_entries_json, modules_json, ctx) -> prefix_string :
//       using_entries_json を modules_json を使ってパス解決し、ファイル読まずに
//       「prefix 文字列（空でよい）を組み立てるための情報」を返すスコープ（MVPは空文字返しでOK）。
// - ポリシー: IO/ファイル読み込みは絶対にしない。より重い処理は entry/pipeline 側の責務。

static box UsingResolveSSOTBox {
  /// Resolve a module name to a file path string (or null when not found).
  /// name: requested module name (e.g., "hako.mir.builder.internal.lower_return_int")
  /// ctx : optional map for extra hints. Supported keys (all optional):
  ///   - modules: Map<String,String> (exact name → path)
  ///   - using_paths: Array<String> (search bases; no IO here, used only for future hints)
  ///   - cwd: String (caller context dir)
  method resolve(name, ctx) {
    if name == null { return null }
    // Strictly pure: do not access filesystem here. Consume only provided hints.
    // 1) modules mapping has priority
    if ctx != null {
      local m = ctx.get("modules");
      if m != null {
        local hit = m.get(name);
        if hit != null { return hit }
      }
      // 2) Relative hint (optional): synthesize a likely path using using_paths/cwd
      //    Gate via ctx.relative_hint == "1" to avoid behavior changes unless explicitly enabled.
      local rh = ctx.get("relative_hint");
      if rh != null && rh == "1" {
        local leaf = me._dot_to_slash(name) + ".hako";
        // prefer cwd
        local cwd = ctx.get("cwd");
        if cwd != null {
          return me._join_path(cwd, leaf)
        }
        local ups = ctx.get("using_paths");
        if ups != null {
          local i = 0; while i < ups.size() {
            local base = ups.get(i);
            return me._join_path(base, leaf)
          i = i + 1 }
        }
      }
    }
    // No IO side effects in MVP
    return null
  }

  _dot_to_slash(s) { return s.replace(".", "/") }
  _join_path(base, leaf) {
    if base == null { return leaf }
    if base.endsWith("/") { return base + leaf }
    return base + "/" + leaf
  }

  // Merge modules_json + using_entries_json into resolved modules table (pure, no IO).
  resolve_modules(modules_json, using_entries_json, ctx) {
    // MVP: just echo back modules_json (keep behavior unchanged) to establish interface.
    return modules_json
  }

  // Build prefix string from using_entries_json with modules_json (pure, no IO).
  resolve_prefix(using_entries_json, modules_json, ctx) {
    // MVP: no file read, so prefix is empty. Interface is reserved for Stage‑1 entry.
    return ""
  }
}
