///|
/// Administrative state used by placement algorithms.
pub(all) enum NodeStatus {
  Active
  Draining
  Offline
} derive(Debug, Eq)

///|
/// Structural problem in a topology snapshot.
pub(all) enum TopologyIssue {
  EmptyNodeId(Int)
  DuplicateNodeId(String)
  EmptyZone(String)
  EmptyRack(String)
} derive(Debug, Eq)

///|
/// Built-in owner-selection strategy.
pub(all) enum PlacementAlgorithm {
  ConsistentRing
  Rendezvous
  TopologyAware
} derive(Debug, Eq)

///|
/// A storage or compute node participating in shard placement.
pub(all) struct ShardNode {
  id : String
  weight : Int
  virtual_nodes : Int
  zone : String
  rack : String
  status : NodeStatus
} derive(Debug, Eq)

///|
/// One key and its ordered owner list.
pub(all) struct KeyPlacement {
  key : String
  owners : Array[String]
  complete : Bool
  message : String
} derive(Debug, Eq)

///|
/// Why an ownership assignment changed.
pub(all) enum MoveKind {
  PrimaryChanged
  ReplicaAdded
  ReplicaRemoved
} derive(Debug, Eq)

///|
/// One planned ownership change between topology snapshots.
pub(all) struct ShardMove {
  key : String
  from_node : String?
  to_node : String?
  kind : MoveKind
} derive(Debug, Eq)

///|
/// Aggregate migration evidence.
pub(all) struct MigrationPlan {
  keys : Int
  moved_keys : Int
  unchanged_keys : Int
  moves : Array[ShardMove]
} derive(Debug, Eq)

///|
/// Ordered safety phase for an executable shard migration.
pub(all) enum MigrationPhase {
  AddTarget
  Backfill
  Verify
  SwitchPrimary
  RemoveSource
} derive(Debug, Eq)

///|
/// One idempotent operation in a safe migration workflow.
pub(all) struct MigrationAction {
  key : String
  phase : MigrationPhase
  from_node : String?
  to_node : String?
} derive(Debug, Eq)

///|
/// A deterministic group of actions that may execute concurrently.
pub(all) struct MigrationWave {
  index : Int
  phase : MigrationPhase
  actions : Array[MigrationAction]
} derive(Debug, Eq)

///|
/// Executable migration workflow with explicit safety and pressure budgets.
pub(all) struct SafeMigrationPlan {
  keys : Int
  migrating_keys : Int
  blocked_keys : Array[String]
  actions : Array[MigrationAction]
  waves : Array[MigrationWave]
  max_actions_per_wave : Int
  max_actions_per_node : Int
} derive(Debug, Eq)

///|
/// Per-node ownership count.
pub(all) struct NodeLoad {
  node_id : String
  primary_keys : Int
  replica_keys : Int
} derive(Debug, Eq)

///|
/// Distribution quality summary.
pub(all) struct DistributionReport {
  keys : Int
  replicas : Int
  nodes : Array[NodeLoad]
  min_primary : Int
  max_primary : Int
  spread : Int
  incomplete_placements : Int
  zone_violations : Int
  rack_violations : Int
} derive(Debug, Eq)

///|
/// Hard ownership budgets for one node during a batch placement run.
/// A missing node from the capacity list is treated as unbounded.
pub(all) struct NodeCapacity {
  node_id : String
  max_primary : Int
  max_replicas : Int
} derive(Debug, Eq)

///|
/// Observed ownership and the configured budgets for one node.
pub(all) struct CapacityLoad {
  node_id : String
  primary_keys : Int
  replica_keys : Int
  max_primary : Int
  max_replicas : Int
} derive(Debug, Eq)

///|
/// Deterministic batch placement result with explicit admission failures.
pub(all) struct CapacityPlan {
  placements : Array[KeyPlacement]
  rejected_keys : Array[String]
  loads : Array[CapacityLoad]
  replicas : Int
} derive(Debug, Eq)

///|
/// A signed update to one node's ownership budgets.
pub(all) struct CapacityAdjustment {
  node_id : String
  primary_delta : Int
  replica_delta : Int
} derive(Debug, Eq)

///|
/// Durable progress marker for resuming a safe migration by wave.
pub(all) struct MigrationCheckpoint {
  completed_waves : Int
} derive(Debug, Eq)

///|
pub fn NodeCapacity::new(
  node_id : String,
  max_primary : Int,
  max_replicas : Int,
) -> NodeCapacity {
  {
    node_id,
    max_primary: if max_primary < 0 {
      0
    } else {
      max_primary
    },
    max_replicas: if max_replicas < 0 {
      0
    } else {
      max_replicas
    },
  }
}

///|
/// Creates a normalized active node.
pub fn ShardNode::new(
  id : String,
  zone? : String = "default-zone",
  rack? : String = "default-rack",
  weight? : Int = 1,
  virtual_nodes? : Int = 64,
) -> ShardNode {
  {
    id,
    weight: if weight < 1 {
      1
    } else {
      weight
    },
    virtual_nodes: if virtual_nodes < 1 {
      1
    } else {
      virtual_nodes
    },
    zone,
    rack,
    status: Active,
  }
}

///|
/// Returns a copy with a new administrative state.
pub fn ShardNode::with_status(
  self : ShardNode,
  status : NodeStatus,
) -> ShardNode {
  { ..self, status, }
}

///|
/// Returns whether the node can receive new ownership.
pub fn ShardNode::is_eligible(self : ShardNode) -> Bool {
  self.status == Active
}