// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

///|
/// Render one bound value as a MySQL SQL literal (UTF-8 bytes). Strings are
/// single-quoted with every metacharacter backslash-escaped; blobs use the
/// `x'…'` hex form; NULL/booleans/numbers are their plain literals.
fn render_value(v : @moondb.Value) -> Bytes {
  match v {
    Null => b"NULL"
    Bool(true) => b"1"
    Bool(false) => b"0"
    Int(i) => string_to_bytes(i.to_string())
    Int64(i) => string_to_bytes(i.to_string())
    Double(d) => string_to_bytes(d.to_string())
    Text(s) => {
      let buf = Buffer()
      buf.write_byte(b'\'')
      escape_into(buf, string_to_bytes(s))
      buf.write_byte(b'\'')
      buf.to_bytes()
    }
    Blob(bytes) => {
      let buf = Buffer()
      buf.write_byte(b'x')
      buf.write_byte(b'\'')
      let digits = "0123456789abcdef"
      for i in 0..> 4].to_int().to_byte())
        buf.write_byte(digits[c & 0xF].to_int().to_byte())
      }
      buf.write_byte(b'\'')
      buf.to_bytes()
    }
  }
}

///|
/// Append `bytes`, backslash-escaping the characters that would otherwise break
/// out of a single-quoted string literal. Because escaping happens on UTF-8
/// bytes and every escaped character is ASCII, multibyte text passes through
/// untouched.
fn escape_into(buf : Buffer, bytes : Bytes) -> Unit {
  for i in 0.. Bytes raise MysqlError {
  let src = string_to_bytes(sql)
  let n = src.length()
  let out = Buffer()
  let mut i = 0
  let mut pi = 0
  let mut in_single = false
  let mut in_double = false
  let mut in_back = false
  while i < n {
    let b = src[i].to_int()
    if in_single {
      out.write_byte(src[i])
      if b == 0x5C && i + 1 < n {
        i += 1
        out.write_byte(src[i])
      } else if b == 0x27 {
        in_single = false
      }
      i += 1
    } else if in_double {
      out.write_byte(src[i])
      if b == 0x5C && i + 1 < n {
        i += 1
        out.write_byte(src[i])
      } else if b == 0x22 {
        in_double = false
      }
      i += 1
    } else if in_back {
      out.write_byte(src[i])
      if b == 0x60 {
        in_back = false
      }
      i += 1
    } else if b == 0x27 {
      in_single = true
      out.write_byte(src[i])
      i += 1
    } else if b == 0x22 {
      in_double = true
      out.write_byte(src[i])
      i += 1
    } else if b == 0x60 {
      in_back = true
      out.write_byte(src[i])
      i += 1
    } else if b == 0x3F {
      if pi >= params.length() {
        raise ProtocolError(
          "too many ? placeholders for the " +
          params.length().to_string() +
          " parameter(s) supplied",
        )
      }
      out.write_bytes(render_value(params[pi])[:])
      pi += 1
      i += 1
    } else {
      out.write_byte(src[i])
      i += 1
    }
  }
  if pi != params.length() {
    raise ProtocolError(
      "parameter count mismatch: bound " +
      pi.to_string() +
      " placeholder(s) but " +
      params.length().to_string() +
      " parameter(s) were supplied",
    )
  }
  out.to_bytes()
}