// sf_serializer.mbt — Canonical structured field serialization.
//
// Implements the canonical serialization rules of RFC 9651 §4.2 exactly so
// that a parsed-then-serialized signature field round-trips to the same bytes.
// This byte-exactness is required for RFC 9421 `@signature-params` (the final
// line of every signature base).
//
// Internal serialization functions raise `HsError`; the public entry points
// convert to `Result`.

///|
/// Serializes a string value with escaping.
pub fn serialize_sf_string(v : String) -> Result[String, HsError] {
  Ok(serialize_sf_string_raise(v)) catch {
    e => Err(e)
  }
}

///|
/// Internal: serializes a string with escaping.
fn serialize_sf_string_raise(v : String) -> String raise HsError {
  let out = Buffer::Buffer(size_hint=v.length() + 2)
  out.write_char_utf8('"')
  for ch in v {
    if ch == '"' || ch == '\\' {
      out.write_char_utf8('\\')
    }
    out.write_char_utf8(ch)
  }
  out.write_char_utf8('"')
  buffer_to_string(out)
}

///|
/// Serializes a token value, validating the token grammar.
pub fn serialize_sf_token(v : String) -> Result[String, HsError] {
  try {
    if !is_tchar(v) {
      raise hs_error(
        StructuredFieldParsing,
        InvalidStructuredField,
        "invalid token in serialization",
      )
    }
    Ok(v)
  } catch {
    e => Err(e)
  }
}

///|
/// Serializes an integer in canonical decimal form.
pub fn serialize_sf_integer(v : Int64) -> Result[String, HsError] {
  Ok(v.to_string())
}

///|
/// Serializes a boolean as `?0` or `?1`.
pub fn serialize_sf_boolean(v : Bool) -> Result[String, HsError] {
  if v {
    Ok("?1")
  } else {
    Ok("?0")
  }
}

///|
/// Serializes a byte sequence with standard padded base64.
pub fn serialize_sf_byte_sequence(v : Bytes) -> Result[String, HsError] {
  Ok(":" + base64_encode_bytes(v) + ":")
}

///|
/// Serializes a bare item.
pub fn serialize_sf_bare_item(v : SfBareItem) -> Result[String, HsError] {
  match v {
    SfString(s) => serialize_sf_string(s)
    SfToken(t) => serialize_sf_token(t)
    SfInteger(i) => serialize_sf_integer(i)
    SfByteSequence(b) => serialize_sf_byte_sequence(b)
    SfBoolean(b) => serialize_sf_boolean(b)
  }
}

///|
/// Internal: serializes a bare item, raising on failure.
fn serialize_sf_bare_item_raise(v : SfBareItem) -> String raise HsError {
  match v {
    SfString(s) => serialize_sf_string_raise(s)
    SfToken(t) =>
      if !is_tchar(t) {
        raise hs_error(
          StructuredFieldParsing,
          InvalidStructuredField,
          "invalid token in serialization",
        )
      } else {
        t
      }
    SfInteger(i) => i.to_string()
    SfByteSequence(b) => ":" + base64_encode_bytes(b) + ":"
    SfBoolean(b) => if b { "?1" } else { "?0" }
  }
}

///|
/// Internal: serializes a parameter list. Parameters with a `?1` value
/// serialize as a bare `;name` (RFC 9651 §4.2.5).
fn serialize_sf_parameters_raise(
  parameters : Array[SfParameter],
) -> String raise HsError {
  let out = Buffer::Buffer()
  for p in parameters {
    out.write_char_utf8(';')
    out.write_string_utf8(p.name)
    if !(p.value is SfBoolean(true)) {
      out.write_char_utf8('=')
      out.write_string_utf8(serialize_sf_bare_item_raise(p.value))
    }
  }
  buffer_to_string(out)
}

///|
/// Internal: serializes an item (bare item plus parameters).
fn serialize_sf_item_raise(v : SfItem) -> String raise HsError {
  let base = serialize_sf_bare_item_raise(v.value)
  let params = serialize_sf_parameters_raise(v.parameters)
  base + params
}

///|
/// Internal: serializes an inner list.
fn serialize_sf_inner_list_raise(v : SfInnerList) -> String raise HsError {
  let out = Buffer::Buffer()
  out.write_char_utf8('(')
  for i, item in v.items {
    if i > 0 {
      out.write_char_utf8(' ')
    }
    out.write_string_utf8(serialize_sf_item_raise(item))
  }
  out.write_char_utf8(')')
  out.write_string_utf8(serialize_sf_parameters_raise(v.parameters))
  buffer_to_string(out)
}

///|
/// Internal: serializes a dictionary member with its leading `key=` prefix
/// elided when the value is an implied-true boolean.
fn serialize_sf_member_raise(
  key : String,
  member : SfMember,
) -> String raise HsError {
  match member {
    ItemMember(item) =>
      if item.value is SfBoolean(true) && item.parameters.is_empty() {
        key
      } else {
        key + "=" + serialize_sf_item_raise(item)
      }
    InnerListMember(il) => key + "=" + serialize_sf_inner_list_raise(il)
  }
}

///|
/// Internal: serializes an ordered dictionary.
fn serialize_sf_dictionary_raise(
  entries : Array[SfDictionaryEntry],
) -> String raise HsError {
  let out = Buffer::Buffer()
  for i, entry in entries {
    if i > 0 {
      out.write_string_utf8(", ")
    }
    out.write_string_utf8(serialize_sf_member_raise(entry.key, entry.value))
  }
  buffer_to_string(out)
}

///|
/// Serializes an item.
pub fn serialize_sf_item(v : SfItem) -> Result[String, HsError] {
  Ok(serialize_sf_item_raise(v)) catch {
    e => Err(e)
  }
}

///|
/// Serializes an inner list.
pub fn serialize_sf_inner_list(v : SfInnerList) -> Result[String, HsError] {
  Ok(serialize_sf_inner_list_raise(v)) catch {
    e => Err(e)
  }
}

///|
/// Serializes a dictionary.
pub fn serialize_sf_dictionary(
  entries : Array[SfDictionaryEntry],
) -> Result[String, HsError] {
  Ok(serialize_sf_dictionary_raise(entries)) catch {
    e => Err(e)
  }
}