// signature_params.mbt — Signature metadata parameters (RFC 9421 §2.3).
//
// `created`, `expires`, `keyid`, `alg`, `nonce`, and `tag` are the defined
// signature metadata parameters. Unknown parameters are preserved as
// `extensions` so that a parser/serializer round-trip keeps them intact; a
// `VerificationPolicy` decides whether to accept them.

///|
/// The signature metadata parameters of one signature.
pub(all) struct SignatureParameters {
  /// `created` — UNIX timestamp of signature creation (Integer).
  created : Int64?
  /// `expires` — UNIX timestamp after which the signature is invalid.
  expires : Int64?
  /// `keyid` — identifier for the key material (String).
  keyid : String?
  /// `alg` — the signature algorithm name (String).
  alg : String?
  /// `nonce` — a unique value for replay protection (String).
  nonce : String?
  /// `tag` — an application-specific tag (String).
  tag : String?
  /// Unknown/extension parameters, preserved in order.
  extensions : Array[SfParameter]
}

///|
/// An empty parameter set.
pub fn SignatureParameters::new() -> SignatureParameters {
  {
    created: None,
    expires: None,
    keyid: None,
    alg: None,
    nonce: None,
    tag: None,
    extensions: Array::new(),
  }
}

///|
/// Parses a parameter list into `SignatureParameters`.
///
/// Unknown parameters are stored in `extensions` (in order). Duplicate
/// defined parameters are rejected. `created`/`expires` must be integers;
/// `keyid`/`alg`/`nonce`/`tag` must be strings.
pub fn parse_signature_parameters(
  params : Array[SfParameter],
) -> Result[SignatureParameters, HsError] {
  Ok(parse_signature_parameters_raise(params)) catch {
    e => Err(e)
  }
}

///|
/// Internal: parses signature parameters, raising on violation.
fn parse_signature_parameters_raise(
  params : Array[SfParameter],
) -> SignatureParameters raise HsError {
  let mut created : Int64? = None
  let mut expires : Int64? = None
  let mut keyid : String? = None
  let mut alg : String? = None
  let mut nonce : String? = None
  let mut tag : String? = None
  let extensions : Array[SfParameter] = Array::new()
  for p in params {
    match p.name {
      "created" =>
        match p.value {
          SfInteger(v) =>
            if created is Some(_) {
              raise hs_error(
                SignatureInputParsing,
                InvalidSignatureInput,
                "duplicate created parameter",
              )
            } else {
              created = Some(v)
            }
          _ =>
            raise hs_error(
              SignatureInputParsing,
              InvalidSignatureInput,
              "created must be an integer",
            )
        }
      "expires" =>
        match p.value {
          SfInteger(v) =>
            if expires is Some(_) {
              raise hs_error(
                SignatureInputParsing,
                InvalidSignatureInput,
                "duplicate expires parameter",
              )
            } else {
              expires = Some(v)
            }
          _ =>
            raise hs_error(
              SignatureInputParsing,
              InvalidSignatureInput,
              "expires must be an integer",
            )
        }
      "keyid" =>
        match p.value {
          SfString(v) =>
            if keyid is Some(_) {
              raise hs_error(
                SignatureInputParsing,
                InvalidSignatureInput,
                "duplicate keyid parameter",
              )
            } else {
              keyid = Some(v)
            }
          _ =>
            raise hs_error(
              SignatureInputParsing,
              InvalidSignatureInput,
              "keyid must be a string",
            )
        }
      "alg" =>
        match p.value {
          SfString(v) =>
            if alg is Some(_) {
              raise hs_error(
                SignatureInputParsing,
                InvalidSignatureInput,
                "duplicate alg parameter",
              )
            } else {
              alg = Some(v)
            }
          _ =>
            raise hs_error(
              SignatureInputParsing,
              InvalidSignatureInput,
              "alg must be a string",
            )
        }
      "nonce" =>
        match p.value {
          SfString(v) =>
            if nonce is Some(_) {
              raise hs_error(
                SignatureInputParsing,
                InvalidSignatureInput,
                "duplicate nonce parameter",
              )
            } else {
              nonce = Some(v)
            }
          _ =>
            raise hs_error(
              SignatureInputParsing,
              InvalidSignatureInput,
              "nonce must be a string",
            )
        }
      "tag" =>
        match p.value {
          SfString(v) =>
            if tag is Some(_) {
              raise hs_error(
                SignatureInputParsing,
                InvalidSignatureInput,
                "duplicate tag parameter",
              )
            } else {
              tag = Some(v)
            }
          _ =>
            raise hs_error(
              SignatureInputParsing,
              InvalidSignatureInput,
              "tag must be a string",
            )
        }
      _ => extensions.push({ name: p.name, value: p.value })
    }
  }
  { created, expires, keyid, alg, nonce, tag, extensions }
}

///|
/// Serializes signature parameters canonically in the order
/// `created`, `expires`, `keyid`, `nonce`, `alg`, `tag`, then extensions.
/// This order reproduces every RFC 9421 Appendix B example byte-for-byte.
pub fn serialize_signature_parameters(
  params : SignatureParameters,
) -> Result[String, HsError] {
  Ok(serialize_signature_parameters_raise(params)) catch {
    e => Err(e)
  }
}

///|
/// Internal: canonical serialization, raising on failure.
fn serialize_signature_parameters_raise(
  params : SignatureParameters,
) -> String raise HsError {
  let out = Buffer::Buffer()
  if params.created is Some(v) {
    out.write_string_utf8(";created=" + v.to_string())
  }
  if params.expires is Some(v) {
    out.write_string_utf8(";expires=" + v.to_string())
  }
  if params.keyid is Some(v) {
    out.write_string_utf8(";keyid=" + serialize_sf_string_raise(v))
  }
  if params.nonce is Some(v) {
    out.write_string_utf8(";nonce=" + serialize_sf_string_raise(v))
  }
  if params.alg is Some(v) {
    out.write_string_utf8(";alg=" + serialize_sf_string_raise(v))
  }
  if params.tag is Some(v) {
    out.write_string_utf8(";tag=" + serialize_sf_string_raise(v))
  }
  for p in params.extensions {
    out.write_char_utf8(';')
    out.write_string_utf8(p.name)
    out.write_char_utf8('=')
    out.write_string_utf8(serialize_sf_bare_item_raise(p.value))
  }
  buffer_to_string(out)
}