// signature_input.mbt — The Signature-Input HTTP field (RFC 9421 §4.1).
//
// Signature-Input is a Dictionary Structured Field. Each member key is a
// signature label; each member value is an Inner List of covered component
// strings plus the signature metadata parameters. Parsing preserves order
// everywhere because the signature base depends on it.

///|
/// A single signature's metadata from the Signature-Input field.
pub(all) struct SignatureInputEntry {
  label : String
  covered_components : Array[CoveredComponent]
  parameters : SignatureParameters
}

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

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

///|
/// Parses a raw Signature-Input field value into a `SignatureInput`.
///
/// The dictionary order, inner-list order, and parameter order are all
/// preserved. Duplicate labels are rejected. Each label must be a valid
/// dictionary key.
pub fn parse_signature_input(
  input : String,
  limits : Limits,
) -> Result[SignatureInput, HsError] {
  try {
    let entries = parse_signature_input_raise(input, limits)
    Ok({ entries, })
  } catch {
    e => Err(e)
  }
}

///|
/// Internal: parses Signature-Input, raising on violation.
fn parse_signature_input_raise(
  input : String,
  limits : Limits,
) -> Array[SignatureInputEntry] raise HsError {
  limits.check_signature_field_size(input.length(), "Signature-Input")
  let dict = match parse_sf_dictionary_string(input, limits) {
    Ok(d) => d
    Err(e) => raise e
  }
  let entries : Array[SignatureInputEntry] = Array::new()
  let seen : Array[String] = Array::new()
  for entry in dict {
    for s in seen {
      if s == entry.key {
        raise hs_error(
          SignatureInputParsing,
          DuplicateLabel,
          "duplicate signature label: " + entry.key,
        )
      }
    }
    seen.push(entry.key)
    let il = match entry.value {
      InnerListMember(il) => il
      ItemMember(_) =>
        raise hs_error(
          SignatureInputParsing,
          InvalidSignatureInput,
          "Signature-Input member must be an Inner List",
        )
    }
    let components = match
      parse_covered_components(il.items, limits.max_components_per_signature) {
      Ok(cs) => cs
      Err(e) => raise e
    }
    let parameters = match parse_signature_parameters(il.parameters) {
      Ok(p) => p
      Err(e) => raise e
    }
    entries.push({
      label: entry.key,
      covered_components: components,
      parameters,
    })
  }
  entries
}

///|
/// Serializes a Signature-Input field back to its canonical form.
pub fn serialize_signature_input(
  input : SignatureInput,
) -> Result[String, HsError] {
  Ok(serialize_signature_input_raise(input)) catch {
    e => Err(e)
  }
}

///|
/// Internal: serializes a Signature-Input field, raising on failure.
fn serialize_signature_input_raise(
  input : SignatureInput,
) -> String raise HsError {
  let out = Buffer::Buffer()
  for i, entry in input.entries {
    if i > 0 {
      out.write_string_utf8(", ")
    }
    out.write_string_utf8(entry.label)
    out.write_char_utf8('=')
    out.write_string_utf8(serialize_entry_inner_list_raise(entry))
  }
  buffer_to_string(out)
}

///|
/// Internal: serializes one entry's inner list (covered components plus
/// signature parameters), raising on failure.
fn serialize_entry_inner_list_raise(
  entry : SignatureInputEntry,
) -> String raise HsError {
  let items : Array[SfItem] = Array::new()
  for cc in entry.covered_components {
    items.push(covered_component_to_sf_item(cc))
  }
  let parameters : Array[SfParameter] = Array::new()
  signature_parameters_to_sf_params(entry.parameters, parameters)
  let il = { items, parameters }
  serialize_sf_inner_list_raise(il)
}

///|
/// Appends signature parameters as SF parameters (for serialization).
fn signature_parameters_to_sf_params(
  params : SignatureParameters,
  out : Array[SfParameter],
) -> Unit {
  if params.created is Some(v) {
    out.push({ name: "created", value: SfInteger(v) })
  }
  if params.expires is Some(v) {
    out.push({ name: "expires", value: SfInteger(v) })
  }
  if params.keyid is Some(v) {
    out.push({ name: "keyid", value: SfString(v) })
  }
  if params.nonce is Some(v) {
    out.push({ name: "nonce", value: SfString(v) })
  }
  if params.alg is Some(v) {
    out.push({ name: "alg", value: SfString(v) })
  }
  if params.tag is Some(v) {
    out.push({ name: "tag", value: SfString(v) })
  }
  for e in params.extensions {
    out.push({ name: e.name, value: e.value })
  }
}

///|
/// Returns the entry with the given label, or `MissingSignatureInput`.
pub fn get_signature_input(
  input : SignatureInput,
  label : String,
) -> Result[SignatureInputEntry, HsError] {
  for entry in input.entries {
    if entry.label == label {
      return Ok(entry)
    }
  }
  Err(
    hs_error(
      SignatureInputParsing,
      MissingSignatureInput,
      "no Signature-Input entry for label: " + label,
    ),
  )
}

///|
/// Appends an entry, rejecting a duplicate label.
pub fn append_signature_input(
  input : SignatureInput,
  entry : SignatureInputEntry,
) -> Result[SignatureInput, HsError] {
  for e in input.entries {
    if e.label == entry.label {
      return Err(
        hs_error(
          SignatureInputParsing,
          DuplicateLabel,
          "duplicate signature label: " + entry.label,
        ),
      )
    }
  }
  let entries = input.entries
  entries.push(entry)
  Ok({ entries, })
}

///|
/// Validates a parsed Signature-Input against RFC 9421 structural rules and
/// the configured limits:
/// - label uniqueness (already enforced at parse time);
/// - `expires` must not be earlier than `created`;
/// - `keyid`/`nonce`/`tag` length limits;
/// - signature count limit.
pub fn validate_signature_input(
  input : SignatureInput,
  limits : Limits,
) -> Result[Unit, HsError] {
  try {
    if input.entries.length() > limits.max_signature_count {
      raise hs_error(
        SignatureInputParsing,
        TooManySignatures,
        "too many signatures",
      )
    }
    for entry in input.entries {
      let p = entry.parameters
      if p.created is Some(c) && p.expires is Some(e) {
        if e < c {
          raise hs_error(
            SignatureInputParsing,
            InvalidTimestamp,
            "expires is earlier than created for label: " + entry.label,
          )
        }
      }
      if p.keyid is Some(k) {
        limits.check_keyid_length(k)
      }
      if p.nonce is Some(n) {
        limits.check_nonce_length(n)
      }
      if p.tag is Some(t) {
        limits.check_tag_length(t)
      }
    }
    Ok(())
  } catch {
    e => Err(e)
  }
}