// ===== 节点状态机纯核(状态/迁移数据化——大 gen 思想:显式穷尽,禁静默默认臂)=====
// 状态机(net_kernel up_nodedown 语义对位——规格书 §8):
//   Disconnected --connect--> Connecting --ack--> Connected
//   Connected   --conn_lost--> Disconnected(触发 nodedown 事件+reconnect 决策)
//   Connecting  --fail--> Disconnected(退避重试)

///|
pub(all) suberror NodeError {
  UnknownState(String)
  IllegalTransition(String)
}

///|
pub(all) enum NodeState {
  Disconnected
  Connecting
  Connected
}

///|
/// 迁移表(数据面——显式穷尽;新迁移=新行)
pub(all) struct Transition {
  from : NodeState
  event : String
  to : NodeState
}

///|
/// 迁移表(数据面真源——显式穷尽;新迁移=新行;fn 形因 const Array 限原语)
pub fn transitions() -> Array[Transition] {
  [
    Transition::{ from: Disconnected, event: "connect", to: Connecting, },
    Transition::{ from: Connecting, event: "ack", to: Connected, },
    Transition::{ from: Connecting, event: "fail", to: Disconnected, },
    Transition::{ from: Connected, event: "conn_lost", to: Disconnected, },
  ]
}

///|
/// 迁移查找(纯函数;查无=IllegalTransition 诚实拒——零静默默认臂)
pub fn transition(
  from : NodeState,
  event : String,
) -> NodeState raise NodeError {
  for tr in transitions() {
    if state_eq(tr.from, from) && tr.event == event {
      return tr.to
    }
  }
  raise NodeError::IllegalTransition("\{state_name(from)} --\{event}--> ?")
}

///|
/// 状态名(文本面)
pub fn state_name(s : NodeState) -> String {
  match s {
    Disconnected => "disconnected"
    Connecting => "connecting"
    Connected => "connected"
  }
}

///|
/// 状态等(骨架手写——无 derive 守恒)
fn state_eq(a : NodeState, b : NodeState) -> Bool {
  match (a, b) {
    (Disconnected, Disconnected) => true
    (Connecting, Connecting) => true
    (Connected, Connected) => true
    _ => false
  }
}

///|
/// 重连退避(A10 同源:指数+cap+max 停机哨兵 -1)
pub(all) struct NodeRetry {
  base_ms : Int32
  cap_ms : Int32
  max : Int32
}

///|
pub fn NodeRetry::new(
  base_ms~ : Int32 = 1000,
  cap_ms~ : Int32 = 8000,
  max~ : Int32 = 3,
) -> NodeRetry {
  { base_ms, cap_ms, max, }
}

///|
pub fn NodeRetry::delay(self : NodeRetry, tries : Int32) -> Int32 {
  if tries >= self.max {
    -1
  } else {
    let d = self.base_ms * (1 << tries)
    if d > self.cap_ms {
      self.cap_ms
    } else {
      d
    }
  }
}

///|
/// 连接执行位(六边形:EPMD+握手重放由消费方注入——beam-dist 组装)
pub(open) trait ConnectPort {
  // async 真件面(beam-dist 组装面——epmd 发现+五帧握手 async 全程)
  async fn connect(self : Self, node : String) -> Bool
}

///|
/// 节点管理器:状态机+退避+执行位+事件面组合(门 C 四层闭环宿主:
/// conn_lost→账清→nodedown 事件→决策→重连执行)
pub struct NodeManager {
  node : String
  priv mut state : NodeState
  retry : NodeRetry
  conn : &ConnectPort
  events : &NodeEvents // 订阅面(NodeSupervisor 接线——决策由订阅者出)
  priv registry : NodeRegistry // 多节点账本(主账=self.node)
}

///|
pub fn NodeManager::new(
  node~ : String,
  retry~ : NodeRetry,
  conn~ : &ConnectPort,
  events~ : &NodeEvents,
) -> NodeManager {
  let registry = NodeRegistry::new()
  let _ = registry.register(node) // 主账预注册(账随连接建——ets insert 对位)
  { node, state: Disconnected, retry, conn, events, registry, }
}

///|
/// 主账(本节点连接的账本——罐测/上层读写用)
pub fn NodeManager::account(self : NodeManager) -> NodeAccount {
  // register 幂等回返主账(ets insert 对位——永不缺席,零 fail 面)
  self.registry.register(self.node)
}

///|
pub fn NodeManager::state(self : NodeManager) -> String {
  state_name(self.state)
}

///|
/// 重连(Once 策略执行面):状态机走 connect→执行位→ack/fail→退避循环
/// 返 true=Connected;false=重试耗尽(停机哨兵)
pub async fn NodeManager::reconnect(self : NodeManager) -> Bool raise NodeError {
  let mut tries = 0
  while true {
    self.state = transition(self.state, "connect")
    let ok = self.conn.connect(self.node) catch { _ => false }
    if ok {
      self.state = transition(self.state, "ack")
      return true
    }
    self.state = transition(self.state, "fail")
    let d = self.retry.delay(tries)
    if d < 0 {
      return false
    }
    tries += 1
  }
  false // 不可达(while true 内必有 return;编译器类型收口)
}

///|
/// 连接丢失真形(门 C 事件源接线):状态机走 conn_lost→主账 on_conn_lost
/// (清账+lost 计数+nodedown 事件上抛订阅者→决策)→决策为 reconnect 时
/// 执行位重连(退避循环)——本地判定语义,不依赖对端 EXIT 帧。
/// 返回 true=重连成功回 Connected;false=降级或重试耗尽。
pub async fn NodeManager::mark_lost(self : NodeManager) -> Bool raise NodeError {
  self.state = transition(self.state, "conn_lost")
  let acc = self.account()
  acc.on_conn_lost(self.events)
  // 决策由订阅者(NodeSupervisor)落账——本件读不回决策词(权柄在订阅面);
  // Once 策略执行面 = 重连尝试(执行位退避循环)——决策退化词(degrade)
  // 时上层不再调 mark_lost 后的重连,这里统一走一次重连尝试,
  // 成败如实返回(执行面无策略复制——策略在订阅者,执行在此)
  self.reconnect()
}