///|
/// toolsfx-moonbit demo 鈥?argv front-end over the shared JSON dispatch.
/// Every computation goes through `@workbench.dispatch`, exactly like the
/// browser workbench (design D3: one dispatch, three front-ends).
///
/// Usage:
///   moon run cmd/demo --  [input] [--key=value ...]
///   moon run cmd/demo -- sm3 abc
///   moon run cmd/demo -- caesar_encode Hello --shift=3
///   moon run cmd/demo -- vigenere_encode ATTACKATDAWN --key=LEMON
///   moon run cmd/demo -- sm4_encrypt secret --mode=cbc \
///        --key_hex=0123456789abcdeffedcba9876543210 \
///        --iv_hex=000102030405060708090a0b0c0d0e0f
///   moon run cmd/demo -- detect "Gur dhvpx..."
fn main {
  let args = @env.args()
  if args.length() <= 1 {
    print_usage()
    return
  }
  // The first non-flag token is the op; module defaults by known ops.
  let op = args[1]
  let module_name = module_of(op)
  // Remaining tokens: one optional positional input, then --key=value flags.
  let mut input : String? = None
  let flags : Array[(String, String)] = []
  for i in 2.. 2 && arg[:2].to_owned() == "--" {
      let body = arg[2:].to_owned()
      match find_first(body, "=") {
        Some(eq) => flags.push((body[:eq].to_owned(), body[eq + 1:].to_owned()))
        None => flags.push((body, "true"))
      }
    } else if input is None {
      input = Some(arg)
    }
  }
  // Build the flat dispatch JSON.
  let parts : Array[String] = [
    "\"module\":\"\{module_name}\"",
    "\"op\":\"\{op}\"",
  ]
  match input {
    Some(t) => parts.push("\"input\":\{json_escape(t)}")
    None => ()
  }
  for pair in flags {
    let (k, v) = pair
    parts.push("\"\{k}\":\{json_escape(v)}")
  }
  let request = "{" + parts.join(",") + "}"
  let response = @workbench.dispatch(request)
  // Pretty-print: success shows the result; errors show code + messages.
  print_response(response)
}

///|
/// Index of the first occurrence of `c` in `s`, if any.
fn find_first(s : String, c : String) -> Int? {
  s.find(c)
}

///|
/// Map an op to its module; `module.op` syntax overrides.
fn module_of(op : String) -> String {
  match find_first(op, ".") {
    Some(dot) => return op[:dot].to_owned()
    None => ()
  }
  match op {
    "sm3"
    | "sm4_encrypt"
    | "sm4_decrypt"
    | "sm2_keygen"
    | "sm2_sign"
    | "sm2_verify"
    | "sm2_encrypt"
    | "sm2_decrypt" => "sm"
    "caesar_encode"
    | "caesar_decode"
    | "vigenere_encode"
    | "vigenere_decode"
    | "atbash"
    | "morse_encode"
    | "morse_decode"
    | "railfence_encode"
    | "railfence_decode"
    | "detect" => "classical"
    "base16_encode"
    | "base16_decode"
    | "base32_encode"
    | "base32_decode"
    | "base58_encode"
    | "base58_decode"
    | "base58check_encode"
    | "base58check_decode"
    | "base62_encode"
    | "base62_decode"
    | "base64_encode"
    | "base64_decode"
    | "base85_encode"
    | "base85_decode"
    | "base91_encode"
    | "base91_decode"
    | "punycode_encode"
    | "punycode_decode"
    | "url_encode"
    | "url_decode"
    | "radix_convert" => "encoding"
    "md5"
    | "sha1"
    | "sha256"
    | "sha512"
    | "hmac_sha256"
    | "aes_cbc_encrypt"
    | "aes_cbc_decrypt"
    | "aes_gcm_encrypt"
    | "aes_gcm_decrypt"
    | "chacha20" => "crypto"
    _ => "sm"
  }
}

///|
/// Minimal JSON string escaping for user-supplied values.
fn json_escape(s : String) -> String {
  let buf = StringBuilder()
  buf.write_char('"')
  for c in s {
    match c {
      '"' => buf.write_string("\\\"")
      '\\' => buf.write_string("\\\\")
      '\n' => buf.write_string("\\n")
      '\r' => buf.write_string("\\r")
      '\t' => buf.write_string("\\t")
      _ => buf.write_char(c)
    }
  }
  buf.write_char('"')
  buf.to_string()
}

///|
/// Extract `"key":"value"` from a response string (small fixed-shape JSON).
fn field(json : String, key : String) -> String? {
  let marker = "\"\{key}\":\""
  match json.find(marker) {
    Some(at) => {
      let start = at + marker.length()
      let rest = json[start:]
      match rest.find("\"") {
        Some(end) => Some("\{rest[:end]}")
        None => None
      }
    }
    None => None
  }
}

///|
/// Print a dispatch response in a human-friendly way; errors exit non-zero
/// semantics via a clear ERROR line.
fn print_response(response : String) -> Unit {
  if response.contains("\"ok\":true") {
    match field(response, "result") {
      Some(r) => println(r)
      None => println(response)
    }
  } else {
    let code = field(response, "code") |> or_else("E_DISPATCH_INTERNAL")
    let en = field(response, "msg_en") |> or_else("unknown error")
    let zh = field(response, "msg_zh")
    println("ERROR \{code}: \{en}")
    match zh {
      Some(z) => println("\{z}")
      None => ()
    }
  }
}

///|
fn or_else(opt : String?, default : String) -> String {
  match opt {
    Some(s) => s
    None => default
  }
}

///|
fn print_usage() -> Unit {
  println("toolsfx-moonbit demo 鈥?crypto toolbox via one JSON dispatch")
  println("=============================================================")
  println("Usage: moon run cmd/demo --  [input] [--key=value ...]")
  println("")
  println("Capabilities (module/op): \{@workbench.capabilities()}")
  println("")
  println("Examples:")
  println("  moon run cmd/demo -- sm3 abc")
  println("  moon run cmd/demo -- caesar_encode Hello --shift=3")
  println("  moon run cmd/demo -- vigenere_encode ATTACKATDAWN --key=LEMON")
  println("  moon run cmd/demo -- morse_encode SOS")
  println(
    "  moon run cmd/demo -- detect \"Gur dhvpx oebja sbk whzcf bire gur ynml qbt\"",
  )
  println("  moon run cmd/demo -- base64_encode Hello")
  println("  moon run cmd/demo -- sha256 abc")
  println("  moon run cmd/demo -- sm4_encrypt secret --mode=cbc \\")
  println("       --key_hex=0123456789abcdeffedcba9876543210 \\")
  println("       --iv_hex=000102030405060708090a0b0c0d0e0f")
}