// if_match_normalize_macro.hako
// Scaffold: identity expansion for now. Future: introduce join variable and
// canonical If/Match normalization (scrutinee once, guard fused) as documented
// in docs/guides/if-match-normalize.md.

static box MacroBoxSpec {
  name() { return "IfMatchNormalize" }

  expand(json, ctx) {
    using "apps/lib/json_builder.hako" as JB

    // --- helpers copied/adapted from loop_normalize ---
    function parse_value(s, i) {
      local n = s.length()
      if i >= n { return ("" + i) + "#" + "" }
      local ch = s.substring(i, i+1)
      if ch == "\"" {
        local j = i + 1
        loop(j < n) {
          local c = s.substring(j, j+1)
          if c == "\\" {
            j = j + 2
            continue
          }
          if c == "\"" {
            j = j + 1
            break
          }
          j = j + 1
        }
        return ("" + j) + "#" + s.substring(i, j)
      }
      if ch == "{" {
        local depth = 1
        local j = i + 1
        local in_str = false
        loop(j < n && depth > 0) {
          local c = s.substring(j, j+1)
          if c == "\"" {
            local k = j - 1
            local esc = false
            if k >= 0 && s.substring(k, k+1) == "\\" { esc = true }
            if not esc { in_str = not in_str }
            j = j + 1
            continue
          }
          if not in_str {
            if c == "{" { depth = depth + 1 }
            if c == "}" { depth = depth - 1 }
          }
          j = j + 1
        }
        return ("" + j) + "#" + s.substring(i, j)
      }
      if ch == "[" {
        local depth = 1
        local j = i + 1
        local in_str = false
        loop(j < n && depth > 0) {
          local c = s.substring(j, j+1)
          if c == "\"" {
            local k = j - 1
            local esc = false
            if k >= 0 && s.substring(k, k+1) == "\\" { esc = true }
            if not esc { in_str = not in_str }
            j = j + 1
            continue
          }
          if not in_str {
            if c == "[" { depth = depth + 1 }
            if c == "]" { depth = depth - 1 }
          }
          j = j + 1
        }
        return ("" + j) + "#" + s.substring(i, j)
      }
      local j = i
      loop(j < n) {
        local c = s.substring(j, j+1)
        if c == "," || c == "]" || c == "}" || c == "\n" || c == "\r" || c == "\t" || c == " " { break }
        j = j + 1
      }
      return ("" + j) + "#" + s.substring(i, j)
    }

    function pair_idx(pair) {
      local i = 0
      local n = pair.length()
      local val = 0
      loop(i < n) {
        local ch = pair.substring(i, i+1)
        if ch == "#" { break }
        local d = 0
        if ch == "0" { d = 0 }
        if ch == "1" { d = 1 }
        if ch == "2" { d = 2 }
        if ch == "3" { d = 3 }
        if ch == "4" { d = 4 }
        if ch == "5" { d = 5 }
        if ch == "6" { d = 6 }
        if ch == "7" { d = 7 }
        if ch == "8" { d = 8 }
        if ch == "9" { d = 9 }
        val = val * 10 + d
        i = i + 1
      }
      return val
    }

    function pair_json(pair) {
      local i = 0
      local n = pair.length()
      loop(i < n) {
        if pair.substring(i, i+1) == "#" { return pair.substring(i+1, n) }
        i = i + 1
      }
      return ""
    }

    function find_field_pos(obj, key) {
      local tok = "\"" + key + "\":"
      local i = 0
      loop(i + tok.length() <= obj.length()) {
        if obj.substring(i, i + tok.length()) == tok { return i + tok.length() }
        i = i + 1
      }
      return -1
    }

    function has_kind(obj, k) {
      local tok = "\"kind\":\"" + k + "\""
      local i = 0
      loop(i + tok.length() <= obj.length()) {
        if obj.substring(i, i + tok.length()) == tok { return true }
        i = i + 1
      }
      return false
    }

    function split_array(arr_json) {
      // arr_json like "[ ... ]"
      if arr_json.length() < 2 { return [] }
      local inner = arr_json.substring(1, arr_json.length()-1)
      local out = []
      local i = 0
      local n = inner.length()
      local in_str = false
      local d_obj = 0
      local d_arr = 0
      local start = 0
      loop(i < n) {
        local c = inner.substring(i, i+1)
        if c == "\"" {
          local k = i - 1
          local esc = false
          if k >= 0 && inner.substring(k, k+1) == "\\" { esc = true }
          if not esc { in_str = not in_str }
        } else if not in_str {
          if c == "{" { d_obj = d_obj + 1 }
          if c == "}" { d_obj = d_obj - 1 }
          if c == "[" { d_arr = d_arr + 1 }
          if c == "]" { d_arr = d_arr - 1 }
          if c == "," && d_obj == 0 && d_arr == 0 {
            out.push(inner.substring(start, i))
            start = i + 1
          }
        }
        i = i + 1
      }
      if start < n { out.push(inner.substring(start, n)) }
      // trim whitespace
      local j = 0
      loop(j < out.length()) {
        local e = out.get(j)
        local a = 0
        local b = e.length()
        loop(a < b && (e.substring(a,a+1)==" " || e.substring(a,a+1)=="\n" || e.substring(a,a+1)=="\t" || e.substring(a,a+1)=="\r")) { a = a + 1 }
        loop(b > a && (e.substring(b-1,b)==" " || e.substring(b-1,b)=="\n" || e.substring(b-1,b)=="\t" || e.substring(b-1,b)=="\r")) { b = b - 1 }
        out.set(j, e.substring(a,b))
        j = j + 1
      }
      return out
    }

    function get_field(obj, key) {
      local p = find_field_pos(obj, key)
      if p < 0 { return null }
      local pr = parse_value(obj, p)
      return pair_json(pr)
    }

    function replace_field(obj, key, new_val) {
      local tok = "\"" + key + "\":"
      local start = -1
      local i2 = 0
      loop(i2 + tok.length() <= obj.length()) {
        if obj.substring(i2, i2 + tok.length()) == tok {
          start = i2
          break
        }
        i2 = i2 + 1
      }
      if start < 0 { return obj }
      local p = start + tok.length()
      local pr = parse_value(obj, p)
      local endi = pair_idx(pr)
      local prefix = obj.substring(0, start + tok.length())
      local suffix = obj.substring(endi, obj.length())
      return prefix + new_val + suffix
    }

    function peek_to_if_expr(peek_json) {
      local scr = get_field(peek_json, "scrutinee")
      local arms_raw = get_field(peek_json, "arms")
      local arms = split_array(arms_raw)
      local else_expr = get_field(peek_json, "else")
      local current = else_expr
      local idx = arms.length()
      loop(idx > 0) {
        idx = idx - 1
        local arm_json = arms.get(idx)
        local lit_json = get_field(arm_json, "literal")
        local body_json = get_field(arm_json, "body")
      local cond = JB.binary("==", scr, lit_json)
      local then_arr = [body_json]
      local else_arr = null
      if current != null { else_arr = [current] }
      local if_json = JB.if_(cond, then_arr, else_arr)
      current = if_json
      }
      return current
    }

    function rewrite_stmt_node(njson) {
      // Assignment with If RHS → If(assign ...)
      if has_kind(njson, "Assignment") {
        local tgt = get_field(njson, "target")
        local val_json = get_field(njson, "value")
        if val_json != null && has_kind(val_json, "PeekExpr") {
          val_json = peek_to_if_expr(val_json)
          njson = replace_field(njson, "value", val_json)
        }
        if val_json != null && has_kind(val_json, "If") {
          local cond = get_field(val_json, "condition")
          local th = get_field(val_json, "then")
          local el = get_field(val_json, "else")
          // then/else are arrays; take first element as expr
          local th_e = null
          if th != null && th.substring(0,1) == "[" {
            local parts = split_array(th)
            if parts.length() > 0 { th_e = parts.get(0) } else { th_e = JB.literal_null() }
          }
          local el_e = null
          if el == null || el == "null" {
            el_e = null
          } else if el.substring(0,1) == "[" {
            local parts2 = split_array(el)
            if parts2.length() > 0 { el_e = parts2.get(0) } else { el_e = JB.literal_null() }
          }
          local then_s = [ JB.assignment(tgt, th_e) ]
          local else_s = null
          if el_e != null { else_s = [ JB.assignment(tgt, el_e) ] }
          return JB.if_(cond, then_s, else_s)
        }
      }
      // Return with If value → If(Return ...)
      if has_kind(njson, "Return") {
        local val_json = get_field(njson, "value")
        if val_json != null && val_json != "null" && has_kind(val_json, "PeekExpr") {
          val_json = peek_to_if_expr(val_json)
          njson = replace_field(njson, "value", val_json)
        }
        if val_json != null && val_json != "null" && has_kind(val_json, "If") {
          local cond = get_field(val_json, "condition")
          local th = get_field(val_json, "then")
          local el = get_field(val_json, "else")
          local th_e = null
          if th != null && th.substring(0,1) == "[" {
            local parts = split_array(th)
            if parts.length() > 0 { th_e = parts.get(0) } else { th_e = JB.literal_null() }
          }
          local el_e = null
          if el == null || el == "null" {
            el_e = null
          } else if el.substring(0,1) == "[" {
            local parts2 = split_array(el)
            if parts2.length() > 0 { el_e = parts2.get(0) } else { el_e = JB.literal_null() }
          }
          local then_s = [ JB.return_(th_e) ]
          local else_s = null
          if el_e != null { else_s = [ JB.return_(el_e) ] }
          return JB.if_(cond, then_s, else_s)
        }
      }
      // Print with If expression → If(Print ...)
      if has_kind(njson, "Print") {
        local ex_json = get_field(njson, "expression")
        if ex_json != null && has_kind(ex_json, "PeekExpr") {
          ex_json = peek_to_if_expr(ex_json)
          njson = replace_field(njson, "expression", ex_json)
        }
        if ex_json != null && has_kind(ex_json, "If") {
          local cond = get_field(ex_json, "condition")
          local th = get_field(ex_json, "then")
          local el = get_field(ex_json, "else")
          local th_e = null
          if th != null && th.substring(0,1) == "[" {
            local parts = split_array(th)
            if parts.length() > 0 { th_e = parts.get(0) } else { th_e = JB.literal_null() }
          }
          local el_e = null
          if el == null || el == "null" {
            el_e = null
          } else if el.substring(0,1) == "[" {
            local parts2 = split_array(el)
            if parts2.length() > 0 { el_e = parts2.get(0) } else { el_e = JB.literal_null() }
          }
          local then_s = [ JB.print_(th_e) ]
          local else_s = null
          if el_e != null { else_s = [ JB.print_(el_e) ] }
          return JB.if_(cond, then_s, else_s)
        }
      }
      if has_kind(njson, "Local") {
        local inits = get_field(njson, "inits")
        if inits != null && inits.substring(0,1) == "[" {
          local elems = split_array(inits)
          local mutated = 0
          local idx = 0
          loop(idx < elems.length()) {
            local init_json = elems.get(idx)
            if init_json != "null" && has_kind(init_json, "PeekExpr") {
              local replaced = peek_to_if_expr(init_json)
              elems.set(idx, replaced)
              mutated = 1
            }
            idx = idx + 1
          }
          if mutated == 1 {
            njson = replace_field(njson, "inits", "[" + JB.join(elems, ",") + "]")
          }
        }
        return njson
      }
      // Recurse for If/Loop nodes
      if has_kind(njson, "If") {
        local th = get_field(njson, "then")
        local el = get_field(njson, "else")
        if th != null && th.substring(0,1) == "[" {
          local parts = split_array(th)
          local rebuilt = []
          local i3 = 0
          loop(i3 < parts.length()) {
            rebuilt.push(rewrite_stmt_node(parts.get(i3)))
            i3 = i3 + 1
          }
          njson = replace_field(njson, "then", "[" + JB.join(rebuilt, ",") + "]")
        }
        if el != null && el != "null" && el.substring(0,1) == "[" {
          local parts2 = split_array(el)
          local rebuilt2 = []
          local j3 = 0
          loop(j3 < parts2.length()) {
            rebuilt2.push(rewrite_stmt_node(parts2.get(j3)))
            j3 = j3 + 1
          }
          njson = replace_field(njson, "else", "[" + JB.join(rebuilt2, ",") + "]")
        }
        return njson
      }
      if has_kind(njson, "Loop") {
        local body = get_field(njson, "body")
        if body != null && body.substring(0,1) == "[" {
          local parts = split_array(body)
          local rebuilt = []
          local i4 = 0
          loop(i4 < parts.length()) {
            rebuilt.push(rewrite_stmt_node(parts.get(i4)))
            i4 = i4 + 1
          }
          njson = replace_field(njson, "body", "[" + JB.join(rebuilt, ",") + "]")
        }
        return njson
      }
      return njson
    }

    // entry: expect Program at top
    if has_kind(json, "Program") {
      local stm = get_field(json, "statements")
      if stm != null && stm.substring(0,1) == "[" {
        local parts = split_array(stm)
        local rebuilt = []
        local i0 = 0
        loop(i0 < parts.length()) {
          rebuilt.push(rewrite_stmt_node(parts.get(i0)))
          i0 = i0 + 1
        }
        return replace_field(json, "statements", "[" + JB.join(rebuilt, ",") + "]")
      }
    }
    return json
  }
}
