///|
/// CBOR Data Model as defined in RFC 8949.
pub(all) enum CborValue {
  Unsigned(UInt64)
  Integer(Int64)
  Bytes(Bytes)
  Text(String)
  Array(Array[CborValue])
  Map(Array[(CborValue, CborValue)])
  Tag(UInt64, CborValue)
  Simple(Byte)
  Float64(Double)
} derive(Eq, Debug)

///|
/// Errors that can occur during CBOR decoding.
pub(all) suberror CborError {
  UnexpectedEOF
  UnexpectedBreak
  InvalidMajorType(Byte)
  InvalidAdditionalInfo(Byte)
  InvalidIndefiniteChunk(Byte)
  InvalidUtf8
  NonCanonicalEncoding(String)
  TrailingBytes(Int)
  UnsupportedFloatWidth(Byte)
  SemanticError(String)
} derive(Eq, Debug)

///|
pub impl Show for CborError with fn output(_self, logger) {
  logger.write_string("CborError")
}

///|
/// Trait for types that can be serialized to CBOR.
pub trait CborEncode {
  fn to_cbor(Self) -> CborValue
}

///|
/// Trait for types that can be deserialized from CBOR.
pub trait CborDecode {
  fn from_cbor(CborValue) -> Result[Self, CborError]
}

///|
/// Decode a complete CBOR payload into a target type via `CborDecode`.
pub fn[T : CborDecode] decode_as(bytes : Bytes) -> T raise CborError {
  let value = decode(bytes)
  match T::from_cbor(value) {
    Ok(result) => result
    Err(err) => raise err
  }
}

///|
/// Encode Int to CborValue
pub impl CborEncode for Int with fn to_cbor(self) -> CborValue {
  Integer(self.to_int64())
}

///|
/// Encode Int64 to CborValue
pub impl CborEncode for Int64 with fn to_cbor(self) -> CborValue {
  Integer(self)
}

///|
/// Encode UInt64 to CborValue without narrowing the value.
pub impl CborEncode for UInt64 with fn to_cbor(self) -> CborValue {
  Unsigned(self)
}

///|
/// Encode String to CborValue
pub impl CborEncode for String with fn to_cbor(self) -> CborValue {
  Text(self)
}

///|
/// Encode Bool to CborValue
pub impl CborEncode for Bool with fn to_cbor(self) -> CborValue {
  Simple(if self { 21 } else { 20 })
}

///|
/// Encode Double as a CBOR floating-point value.
pub impl CborEncode for Double with fn to_cbor(self) -> CborValue {
  Float64(self)
}

///|
pub impl CborDecode for CborValue with fn from_cbor(value) -> Result[
  CborValue,
  CborError,
] {
  Ok(value)
}

///|
pub impl CborDecode for Bool with fn from_cbor(value) -> Result[Bool, CborError] {
  match value {
    Simple(20) => Ok(false)
    Simple(21) => Ok(true)
    _ => Err(SemanticError("expected CBOR bool"))
  }
}

///|
pub impl CborDecode for Int64 with fn from_cbor(value) -> Result[
  Int64,
  CborError,
] {
  match value {
    Integer(i) => Ok(i)
    Unsigned(i) =>
      if i > 0x7FFFFFFFFFFFFFFFUL {
        Err(SemanticError("unsigned integer exceeds Int64 range"))
      } else {
        Ok(i.reinterpret_as_int64())
      }
    _ => Err(SemanticError("expected CBOR integer"))
  }
}

///|
pub impl CborDecode for UInt64 with fn from_cbor(value) -> Result[
  UInt64,
  CborError,
] {
  match value {
    Unsigned(i) => Ok(i)
    Integer(i) =>
      if i < 0L {
        Err(SemanticError("expected non-negative CBOR integer"))
      } else {
        Ok(i.reinterpret_as_uint64())
      }
    _ => Err(SemanticError("expected CBOR integer"))
  }
}

///|
pub impl CborDecode for String with fn from_cbor(value) -> Result[
  String,
  CborError,
] {
  match value {
    Text(text) => Ok(text)
    _ => Err(SemanticError("expected CBOR text string"))
  }
}

///|
pub impl CborDecode for Bytes with fn from_cbor(value) -> Result[
  Bytes,
  CborError,
] {
  match value {
    Bytes(bytes) => Ok(bytes)
    _ => Err(SemanticError("expected CBOR byte string"))
  }
}

///|
pub impl CborDecode for Double with fn from_cbor(value) -> Result[
  Double,
  CborError,
] {
  match value {
    Float64(number) => Ok(number)
    Integer(i) => Ok(i.to_double())
    Unsigned(i) => Ok(i.to_double())
    _ => Err(SemanticError("expected CBOR number"))
  }
}