// 流程模型 —— LogicFlow JSON → ProcessModel(8 节点类型)
// 移植自 rust parser.rs;spec/01、spec/02

///|
pub(all) struct ProcessModel {
  name : String
  display_name : String
  model_type : String
  expire_time : String?
  persist_mode : String? // ARCHIVE / SYNC
  rel_table_name : String?
  nodes : Array[NodeModel]
  edges : Array[EdgeModel]
} derive(Eq)

///|
pub fn ProcessModel::get_start(self : ProcessModel) -> NodeModel? {
  let mut found : NodeModel? = None
  for n in self.nodes {
    if n.node_type is Start {
      found = Some(n)
    }
  }
  found
}

///|
pub fn ProcessModel::get_node(self : ProcessModel, id : String) -> NodeModel? {
  let mut found : NodeModel? = None
  for n in self.nodes {
    if n.id == id {
      found = Some(n)
    }
  }
  found
}

///|
pub fn ProcessModel::get_nodes_by_type(self : ProcessModel, t : NodeType) -> Array[NodeModel] {
  let out : Array[NodeModel] = []
  for n in self.nodes {
    if n.node_type == t {
      out.push(n)
    }
  }
  out
}

///|
pub fn ProcessModel::get_output_edges(self : ProcessModel, node_id : String) -> Array[EdgeModel] {
  let out : Array[EdgeModel] = []
  for e in self.edges {
    if e.source_node_id == node_id {
      out.push(e)
    }
  }
  out
}

///|
pub fn ProcessModel::get_input_edges(self : ProcessModel, node_id : String) -> Array[EdgeModel] {
  let out : Array[EdgeModel] = []
  for e in self.edges {
    if e.target_node_id == node_id {
      out.push(e)
    }
  }
  out
}

///|
pub fn ProcessModel::get_target_node(self : ProcessModel, edge : EdgeModel) -> NodeModel? {
  self.get_node(edge.target_node_id)
}

///|
/// start 后第一个任务节点
pub fn ProcessModel::get_first_task_node(self : ProcessModel) -> NodeModel? {
  match self.get_start() {
    Some(start) =>
      match self.get_output_edges(start.id).get(0) {
        Some(edge) => self.get_node(edge.target_node_id)
        None => None
      }
    None => None
  }
}

///|
pub fn ProcessModel::is_first_task_node(self : ProcessModel, node_id : String) -> Bool {
  match self.get_first_task_node() {
    Some(first) => first.id == node_id
    None => false
  }
}

///|
pub fn ProcessModel::get_end_nodes(self : ProcessModel) -> Array[NodeModel] {
  self.get_nodes_by_type(End)
}

///|
/// 8 节点类型
pub(all) enum NodeType {
  Start
  Task
  Decision
  Fork
  Join
  End
  Custom
  SubProcess
} derive(Eq, Show)

///|
pub fn NodeType::from_snaker_type(t : String) -> NodeType {
  match t {
    "snaker:start" => Start
    "snaker:task" => Task
    "snaker:decision" => Decision
    "snaker:fork" => Fork
    "snaker:join" => Join
    "snaker:end" => End
    "snaker:subprocess" => SubProcess
    _ => Custom
  }
}

///|
pub fn NodeType::as_str(self : NodeType) -> String {
  match self {
    Start => "start"
    Task => "task"
    Decision => "decision"
    Fork => "fork"
    Join => "join"
    End => "end"
    Custom => "custom"
    SubProcess => "subprocess"
  }
}

///|
pub(all) struct NodeModel {
  id : String
  node_type : NodeType
  display_name : String
  properties : Map[String, Json]
} derive(Eq)

///|
pub fn NodeModel::prop_str(self : NodeModel, key : String) -> String? {
  match self.properties.get(key) {
    Some(v) => @json.as_str(v)
    None => None
  }
}

///|
pub fn NodeModel::prop_i64(self : NodeModel, key : String) -> Int64? {
  match self.properties.get(key) {
    Some(v) => @json.as_i64(v)
    None => None
  }
}

///|
/// 固定参与人
pub fn NodeModel::assignee(self : NodeModel) -> String? {
  self.prop_str("assignee")
}

///|
/// 动态参与人 handler 名
pub fn NodeModel::assignment_handler(self : NodeModel) -> String? {
  self.prop_str("assignmentHandler")
}

///|
/// 会签类型(C4 容错:数字 1/'1'/'ALL'/'COUNTERSIGN' 均认会签,其余 0)
pub fn NodeModel::perform_type(self : NodeModel) -> Int {
  match self.prop_i64("performType") {
    Some(n) => if n == 1L { 1 } else { 0 }
    None =>
      match self.prop_str("performType") {
        Some(s) => {
          let upper = s.to_upper()
          if upper == "1" || upper == "ALL" || upper == "COUNTERSIGN" {
            1
          } else {
            match (try? @string.parse_int(s)) {
              Ok(n) => if n == 1 { 1 } else { 0 }
              Err(_) => 0
            }
          }
        }
        None => 0
      }
  }
}

///|
pub fn NodeModel::task_type(self : NodeModel) -> Int {
  match self.prop_i64("taskType") {
    Some(n) => n.to_int()
    None => 0
  }
}

///|
pub fn NodeModel::countersign_type(self : NodeModel) -> String {
  match self.prop_str("countersignType") {
    Some(s) => s
    None => "PARALLEL"
  }
}

///|
/// 会签完成条件(顶层属性优先,field 子对象兜底)
pub fn NodeModel::countersign_completion_condition(self : NodeModel) -> String? {
  match self.prop_str("countersignCompletionCondition") {
    Some(c) => return Some(c)
    None => ()
  }
  match self.properties.get("field") {
    Some(field_val) =>
      match field_val {
        Json::Object(field_obj) =>
          for k, v in field_obj {
            if k == "countersignCompletionCondition" {
              return @json.as_str(v)
            }
          }
        _ => ()
      }
    None => ()
  }
  None
}

///|
pub fn NodeModel::expr(self : NodeModel) -> String? {
  self.prop_str("expr")
}

///|
pub fn NodeModel::form_key(self : NodeModel) -> String? {
  self.prop_str("form")
}

///|
pub fn NodeModel::candidate_users(self : NodeModel) -> String? {
  self.prop_str("candidateUsers")
}

///|
pub fn NodeModel::candidate_groups(self : NodeModel) -> String? {
  self.prop_str("candidateGroups")
}

///|
/// 字段权限(PERMISSION_* → 1 只读/2 可编辑/3 隐藏,C19)
pub fn NodeModel::field_permissions(self : NodeModel) -> Map[String, Int] {
  let perms : Map[String, Int] = Map::new()
  for k, v in self.properties {
    if k.starts_with("PERMISSION_") {
      match @json.as_i64(v) {
        Some(n) => perms[k] = n.to_int()
        None => ()
      }
    }
  }
  perms
}

///|
pub fn NodeModel::sub_process_name(self : NodeModel) -> String? {
  self.prop_str("subProcessName")
}

///|
pub fn NodeModel::is_countersign(self : NodeModel) -> Bool {
  self.perform_type() == 1
}

///|
pub(all) struct EdgeModel {
  id : String
  source_node_id : String
  target_node_id : String
  label : String?
  properties : Map[String, Json]
} derive(Eq)

///|
pub fn EdgeModel::expr(self : EdgeModel) -> String? {
  match self.properties.get("expr") {
    Some(v) => @json.as_str(v)
    None => None
  }
}

///|
pub fn EdgeModel::source_node_id(self : EdgeModel) -> String {
  self.source_node_id
}

///|
pub fn EdgeModel::target_node_id(self : EdgeModel) -> String {
  self.target_node_id
}

///|
/// LogicFlow JSON → ProcessModel
pub fn parse_model(json_str : String) -> ProcessModel raise @error.JeeflowError {
  let root = @json.parse_json(json_str)
  let name = match @json.get_str(root, "name") {
    Some(s) => s
    None => "unknown"
  }
  let display_name = @json.get_str(root, "displayName").unwrap_or(name)
  let model_type = @json.get_str(root, "type").unwrap_or("approval")
  let expire_time = @json.get_str(root, "expireTime")
  let persist_mode = @json.get_str(root, "persistMode")
  let rel_table_name = match @json.get_str(root, "relTableName") {
    Some(s) => Some(s)
    None => @json.get_str(root, "name")
  }
  // nodes
  let nodes : Array[NodeModel] = []
  match root.value("nodes") {
    Some(nodes_val) =>
      match nodes_val {
        Json::Array(items) =>
          for node_val in items {
            let id = @json.get_str(node_val, "id").unwrap_or("")
            let raw_type = @json.get_str(node_val, "type").unwrap_or("snaker:task")
            let node_type = NodeType::from_snaker_type(raw_type)
            let display_name = match @json.get_str(node_val, "displayName") {
              Some(s) => Some(s)
              None =>
                match node_val.value("text") {
                  Some(t) => @json.get_str(t, "value")
                  None => None
                }
            }
            let properties : Map[String, Json] = Map::new()
            match node_val.value("properties") {
              Some(props) =>
                match props {
                  Json::Object(m) =>
                    for k, v in m {
                      properties[k] = v
                    }
                  _ => ()
                }
              None => ()
            }
            nodes.push({ id, node_type, display_name: display_name.unwrap_or(""), properties })
          }
        _ => ()
      }
    None => ()
  }
  // edges
  let edges : Array[EdgeModel] = []
  match root.value("edges") {
    Some(edges_val) =>
      match edges_val {
        Json::Array(items) =>
          for edge_val in items {
            let id = @json.get_str(edge_val, "id").unwrap_or("")
            let source_node_id = @json.get_str(edge_val, "sourceNodeId").unwrap_or("")
            let target_node_id = @json.get_str(edge_val, "targetNodeId").unwrap_or("")
            let label = match edge_val.value("text") {
              Some(t) => @json.get_str(t, "value")
              None => None
            }
            let properties : Map[String, Json] = Map::new()
            match edge_val.value("properties") {
              Some(props) =>
                match props {
                  Json::Object(m) =>
                    for k, v in m {
                      properties[k] = v
                    }
                  _ => ()
                }
              None => ()
            }
            edges.push({ id, source_node_id, target_node_id, label, properties })
          }
        _ => ()
      }
    None => ()
  }
  { name, display_name, model_type, expire_time, persist_mode, rel_table_name, nodes, edges }
}