///|
/// sttp-shared AttributeMap without Any: per-key slot plus closures.
pub struct AttributeKey[T] {
  priv id : Int32
  priv name : String
  priv slot : Ref[T?]
}

///|
let next_key : Ref[Int32] = { val: 0, }

///|
/// Creates a distinct typed key; identical names do not alias keys.
pub fn[T] AttributeKey::new(name : String) -> AttributeKey[T] {
  next_key.val += 1
  { id: next_key.val, name, slot: { val: None, }, }
}

///|
/// Returns the key's diagnostic name.
pub fn[T] AttributeKey::name(self : AttributeKey[T]) -> String {
  self.name
}

///|
/// Immutable associations of typed keys to local metadata.
pub struct Attributes {
  priv entries : Map[Int32, () -> Unit]
}

///|
/// Creates an empty attribute collection.
pub fn Attributes::empty() -> Attributes {
  { entries: Map([]), }
}

///|
/// Associates a value with a key on an independent copy.
pub fn[T] Attributes::put(
  self : Attributes,
  key : AttributeKey[T],
  value : T,
) -> Attributes {
  let m = self.entries.copy()
  m[key.id] = () => key.slot.val = Some(value)
  { entries: m, }
}

///|
/// Looks up a value with its original type, without consuming it.
pub fn[T] Attributes::get(self : Attributes, key : AttributeKey[T]) -> T? {
  match self.entries.get(key.id) {
    Some(f) => {
      f()
      let v = key.slot.val
      key.slot.val = None
      v
    }
    None => None
  }
}

///|
/// Set by server adapters.
pub let remote_addr : AttributeKey[String] = AttributeKey::new("remote_addr")

///|
/// Adapter-owned scheduling for work that may outlive the response. Invoke
/// during request handling; the adapter owns completion and shutdown policy.
pub let background : AttributeKey[(async () -> Unit) -> Unit] = AttributeKey::new(
  "background",
)

///|
/// A caller's statement that resending this request is safe even after it
/// was written (discord's REPLAY_SAFE_HEADER, without a wire header).
pub let replay_safe : AttributeKey[Bool] = AttributeKey::new("replay_safe")

///|
/// An idempotent method or an explicit replay_safe statement. A header alone
/// cannot establish the remote service's deduplication contract.
pub fn is_idempotent(req : Request) -> Bool {
  req.meth.is_idempotent() || req.attributes.get(replay_safe) == Some(true)
}