// signature_field.mbt — The Signature HTTP field (RFC 9421 §4.2).
//
// Signature is a Dictionary Structured Field. Each member key is a signature
// label; each member value is a Byte Sequence holding the raw signature
// bytes. Signatures are compared byte-wise with `constant_time_equal`; they
// are never converted to UTF-8 text.

///|
/// A single signature value: label plus raw signature bytes.
pub(all) struct SignatureEntry {
  label : String
  value : Bytes
}

///|
/// The parsed Signature field: one entry per signature label.
pub(all) struct SignatureField {
  entries : Array[SignatureEntry]
}

///|
/// Creates an empty Signature field.
pub fn SignatureField::new() -> SignatureField {
  { entries: Array::new() }
}

///|
/// Parses a raw Signature field value into a `SignatureField`.
///
/// Each member value must be a Byte Sequence; the raw bytes are preserved.
/// Duplicate labels are rejected. Base64 decoding errors surface as
/// `InvalidBase64`.
pub fn parse_signature_field(
  input : String,
  limits : Limits,
) -> Result[SignatureField, HsError] {
  try {
    let entries = parse_signature_field_raise(input, limits)
    Ok({ entries, })
  } catch {
    e => Err(e)
  }
}

///|
/// Internal: parses a Signature field, raising on violation.
fn parse_signature_field_raise(
  input : String,
  limits : Limits,
) -> Array[SignatureEntry] raise HsError {
  limits.check_signature_field_size(input.length(), "Signature")
  let dict = match parse_sf_dictionary_string(input, limits) {
    Ok(d) => d
    Err(e) => raise e
  }
  let entries : Array[SignatureEntry] = Array::new()
  let seen : Array[String] = Array::new()
  for entry in dict {
    for s in seen {
      if s == entry.key {
        raise hs_error(
          SignatureFieldParsing,
          DuplicateLabel,
          "duplicate signature label: " + entry.key,
        )
      }
    }
    seen.push(entry.key)
    let bytes = match entry.value {
      ItemMember(item) =>
        match item.value {
          SfByteSequence(b) => b
          _ =>
            raise hs_error(
              SignatureFieldParsing,
              InvalidSignatureField,
              "Signature member must be a Byte Sequence",
            )
        }
      InnerListMember(_) =>
        raise hs_error(
          SignatureFieldParsing,
          InvalidSignatureField,
          "Signature member must be a Byte Sequence",
        )
    }
    entries.push({ label: entry.key, value: bytes })
  }
  entries
}

///|
/// Serializes a Signature field back to its canonical form.
pub fn serialize_signature_field(
  field : SignatureField,
) -> Result[String, HsError] {
  try {
    let out = Buffer::Buffer()
    for i, entry in field.entries {
      if i > 0 {
        out.write_string_utf8(", ")
      }
      out.write_string_utf8(entry.label)
      out.write_char_utf8('=')
      out.write_string_utf8(":" + base64_encode_bytes(entry.value) + ":")
    }
    Ok(buffer_to_string(out))
  } catch {
    e => Err(e)
  }
}

///|
/// Returns the signature bytes for the given label, or `MissingSignature`.
pub fn get_signature(
  field : SignatureField,
  label : String,
) -> Result[Bytes, HsError] {
  for entry in field.entries {
    if entry.label == label {
      return Ok(entry.value)
    }
  }
  Err(
    hs_error(
      SignatureFieldParsing,
      MissingSignature,
      "no Signature entry for label: " + label,
    ),
  )
}

///|
/// Appends a signature entry, rejecting a duplicate label.
pub fn append_signature(
  field : SignatureField,
  entry : SignatureEntry,
) -> Result[SignatureField, HsError] {
  for e in field.entries {
    if e.label == entry.label {
      return Err(
        hs_error(
          SignatureFieldParsing,
          DuplicateLabel,
          "duplicate signature label: " + entry.label,
        ),
      )
    }
  }
  let entries = field.entries
  entries.push({ label: entry.label, value: entry.value })
  Ok({ entries, })
}

///|
/// Validates that the Signature and Signature-Input labels match exactly:
/// every label present in one field must be present in the other. The first
/// mismatched label is reported as `LabelMismatch`.
pub fn validate_signature_labels(
  input : SignatureInput,
  field : SignatureField,
) -> Result[Unit, HsError] {
  for entry in input.entries {
    let mut found = false
    for e in field.entries {
      if e.label == entry.label {
        found = true
        break
      }
    }
    if !found {
      return Err(
        hs_error(
          SignatureFieldParsing,
          LabelMismatch,
          "Signature missing label present in Signature-Input: " + entry.label,
        ),
      )
    }
  }
  for e in field.entries {
    let mut found = false
    for entry in input.entries {
      if entry.label == e.label {
        found = true
        break
      }
    }
    if !found {
      return Err(
        hs_error(
          SignatureInputParsing,
          LabelMismatch,
          "Signature-Input missing label present in Signature: " + e.label,
        ),
      )
    }
  }
  Ok(())
}

///|
/// Returns the number of entries.
pub fn SignatureField::length(self : SignatureField) -> Int {
  self.entries.length()
}