///|
/// Diagnostics contain a stable code, an input path, and an actionable message.
pub(all) suberror PricingError {
  Invalid(code~ : String, path~ : String, message~ : String)
} derive(Debug, Eq)

///|
/// A cumulative inclusive upper bound, or None for the final unbounded tier.
/// Decimal prices are in currency units per one integer usage unit.
pub(all) struct Tier {
  up_to : String?
  unit_price : String
} derive(Debug, Eq)

///|
priv struct CompiledTier {
  up_to : BigInt?
  price : BigInt
}

///|
/// Pricing semantics chosen explicitly in a plan.
pub(all) enum PricingMode {
  Graduated
  Volume
} derive(Debug, Eq)

///|
pub extend PricingMode with Debug::{to_repr}

///|
pub extend PricingMode with Eq::{equal, not_equal}

///|
/// Validated, detached snapshot of a price plan.
pub struct Plan {
  priv id : String
  priv version : String
  priv metric : String
  priv unit : String
  priv currency : String
  priv mode : PricingMode
  priv tiers : Array[CompiledTier]
}

///|
/// Already aggregated usage for one customer, metric, and opaque period label.
pub(all) struct Usage {
  customer : String
  metric : String
  period : String
  quantity : String
} derive(Debug, Eq)

///|
/// Exact allocation for one occupied tier. No line-level monetary rounding.
pub(all) struct Line {
  tier : Int32
  from_exclusive : String
  to_inclusive : String
  quantity : String
  unit_price : String
  subtotal : String
} derive(Debug, Eq)

///|
/// Exact, unsettled calculation. Public records may be inspected and serialized.
pub(all) struct Calculation {
  plan_id : String
  plan_version : String
  currency : String
  unit : String
  usage : Usage
  lines : Array[Line]
  total : String
} derive(Debug, Eq)

///|
/// All amounts are nonnegative; Down/Up mean toward/away from zero.
pub(all) enum Rounding {
  HalfEven
  HalfUp
  Down
  Up
} derive(Debug, Eq)

///|
/// Exactly one rounding operation on the complete total.
/// delta = amount - exact total, and can be negative.
pub(all) struct Settlement {
  currency : String
  scale : Int32
  rounding : Rounding
  minor_units : String
  amount : String
  delta : String
} derive(Debug, Eq)

///|
fn invalid(
  code : String,
  path : String,
  message : String,
) -> Unit raise PricingError {
  raise Invalid(code~, path~, message~)
}