// MetaTableWriter —— spec/10 §5 的元数据驱动写侧(java `persist/meta/MetaTableWriter.java` 同位)。
//
// 形状=**装饰器**:握着基础 writer + 元数据端口,实现同一个 `DynamicTableWriter` trait
// ⇒ 拦截器与 facade 的调用点一个字不用改,装配时把真库 writer 包一层就行
// (java 也是这个形状:`new MetaTableWriter(base, provider)`)。
// 无元数据(provider 返回 None)⇒ 整批委托基础 writer,行为与 1.6.x 一致(spec/10 §3「零破坏」)。

///|
pub(all) struct MetaTableWriter {
  base : &DynamicTableWriter
  meta : DynamicMetaProviderFns
}

///|
pub fn MetaTableWriter::make(
  base : &DynamicTableWriter,
  meta : DynamicMetaProviderFns
) -> MetaTableWriter {
  { base, meta }
}

///|
/// 取元数据;`None`=这张表没配元数据 ⇒ 调用点回落到基础 writer(spec/10 §3)。
async fn MetaTableWriter::load_meta(self : MetaTableWriter, table : String) -> TableMeta? raise @error.JeeflowError {
  (self.meta.load_table_meta)(table)
}

///|
/// 值 → JSON 串(JSON 档写列用)。已是字符串就原样落(java `toJson` 对 String 会多套一层引号,
/// 本栈选择原样——业务把 JSON 文本直接贴进输入框是最常见的形状,多套一层会让读侧解不回对象)。
fn json_column_value(v : Json) -> Json {
  match v {
    Json::String(s) => @json.string_of(s)
    _ => @json.string_of(@json.stringify(v))
  }
}

///|
/// EXPAND:对象字段展开为多列(`expand_fields` 的子字段名 → 表列名)。
/// 非对象值/缺子字段 ⇒ 跳过该列(java `expandInto` 的 `if (!(v instanceof Map)) return`)。
fn expand_into(f : FieldMeta, v : Json, row : Map[String, Json]) -> Unit {
  for sub_name, col in f.expand_fields {
    match @json.get(v, sub_name) {
      Some(fv) => row[col] = fv
      None => ()
    }
  }
}

///|
/// 按元数据把「表单字段为键」的 data 拆成「主表列」+「子表待插清单」。
/// java `insert:52-77`/`update:148-171` 的同一形状:
/// - 遍历**元数据字段**(不是 data),每个字段按 `name` → 回落 `column_name` 找 data 键
///   (本栈拦截器交给 writer 的 data 已经是**列名键**,只按 name 找会在 name≠column_name 时漏)。
/// - NORMAL/JSON/EXPAND 进主表行;ONE2ONE/ONE2MANY 收进子表清单(`sub_in_update=false` 时
///   更新档整档跳过——spec/10 §5「子表不参与中途更新」)。
/// - data 里没被任何字段消费的键(流程上下文 process_instance_id/apply_user_id、状态列、系统列)
///   **直通**,putIfAbsent 语义(java `row.putIfAbsent:75,169`)。
fn MetaTableWriter::split(
  self : MetaTableWriter,
  meta : TableMeta,
  data : Map[String, Json],
  sub_in_update~ : Bool
) -> (Map[String, Json], Array[(FieldMeta, Json)]) {
  let _ = self
  let row : Map[String, Json] = Map([])
  let subs : Array[(FieldMeta, Json)] = []
  for f in meta.fields {
    let key = match find_data_key(data, f.name) {
      Some(k) => Some(k)
      None => find_data_key(data, f.column_name)
    }
    match key {
      None => ()
      Some(k) =>
        match f.storage_type {
          Json => row[f.column_name] = json_column_value(data[k])
          Expand => expand_into(f, data[k], row)
          One2One =>
            if sub_in_update {
              ()
            } else {
              subs.push((f, data[k]))
            }
          One2Many =>
            if sub_in_update {
              ()
            } else {
              subs.push((f, data[k]))
            }
          Normal => row[f.column_name] = data[k]
        }
    }
  }
  for k, v in data {
    if meta.find_field(k) is None && row.get(k) is None {
      row[k] = v
    }
  }
  (row, subs)
}

///|
/// 子表递归插入(java `insertSubTable:101-131`):外键=主表主键,
/// 继承主表 `apply_user_id`(子表显式同名字段优先 ⇒ putIfAbsent),
/// 插自身走**递归**(子表自己的元数据再展开一轮 ⇒ 嵌套 EXPAND/JSON/孙表都能接)。
async fn MetaTableWriter::insert_subs(
  self : MetaTableWriter,
  meta : TableMeta,
  subs : Array[(FieldMeta, Json)],
  parent_pk : Int64,
  parent_data : Map[String, Json]
) -> Unit raise @error.JeeflowError {
  if subs.is_empty() {
    return
  }
  if parent_pk == 0L {
    // java `:103-105`:主表主键缺失就没法定外键,硬失败(不许静默丢子表数据)
    raise @error.Business("主表主键缺失,无法插入子表: \{meta.table_name}")
  }
  let operator_ctx = parent_data.get("apply_user_id")
  // ⚠ 数组的 `for` 给的是「下标, 元素」两值(`for _, pair in`),不是把元组拆开——
  // 直接写 `for f, v in subs` 会让 f 绑成 Int 下标(本轮编译器抓到)。
  for _, pair in subs {
    let (f, v) = pair
    if f.target_table.is_empty() {
      raise @error.Business("字段 \{f.name} 声明 ONE2ONE/ONE2MANY 但未配 targetTable")
    }
    let fk = if f.foreign_key.is_empty() { meta.primary_key } else { f.foreign_key }
    let items : Array[Json] = match f.storage_type {
      One2Many =>
        match @json.as_array(v) {
          Some(arr) => arr
          None => []   // 值不是数组 ⇒ 一条都不插(java `instanceof List` 同判据)
        }
      _ => [v]   // ONE2ONE:单条
    }
    for item in items {
      let row : Map[String, Json] = Map([])
      // 子表行的键=子对象键名原样(java `insertSubRow:123-125`),列名对不上由基础 writer 滤掉
      for _, pair2 in json_obj_entries(item) {
        let (k, kv) = pair2
        row[k] = kv
      }
      row[fk] = @json.number_of(parent_pk)
      match operator_ctx {
        Some(ov) => if row.get("apply_user_id") is None { row["apply_user_id"] = ov }
        None => ()
      }
      let _ = self.insert(f.target_table, row)
    }
  }
}

///|
/// Json 对象的键值对(非对象 ⇒ 空表)。`Json::Object` 在本包**可模式匹配、不可构造**
/// (core 的 `pub enum` 未带 `pub(all)` ⇒ 构造判 read-only type),所以读侧一律走解构。
fn json_obj_entries(v : Json) -> Array[(String, Json)] {
  let out : Array[(String, Json)] = []
  match v {
    Json::Object(obj) =>
      for k, kv in obj {
        out.push((k, kv))
      }
    _ => ()
  }
  out
}

///|
pub impl DynamicTableWriter for MetaTableWriter with fn insert(self, table, data) {
  match self.load_meta(table) {
    None => (self.base).insert(table, data)   // 无元数据:回落现状(spec/10 §3 零破坏)
    Some(meta) => {
      let (row, subs) = self.split(meta, data, sub_in_update=false)
      let pk = (self.base).insert(table, row)
      self.insert_subs(meta, subs, pk, data)
      pk
    }
  }
}

///|
pub impl DynamicTableWriter for MetaTableWriter with fn update(self, table, id, data) {
  match self.load_meta(table) {
    None => (self.base).update(table, id, data)
    Some(meta) => {
      let (row, _) = self.split(meta, data, sub_in_update=true)
      (self.base).update(table, id, row)
    }
  }
}

///|
pub impl DynamicTableWriter for MetaTableWriter with fn update_by_key(self, table, data, key, value) {
  match self.load_meta(table) {
    None => (self.base).update_by_key(table, data, key, value)
    Some(meta) => {
      // spec/10 §5「中途更新」:NORMAL/JSON/EXPAND 参与,ONE2ONE/ONE2MANY 不参与;
      // 上下文/状态列直通(java `update:166-172`)
      let (row, _) = self.split(meta, data, sub_in_update=true)
      (self.base).update_by_key(table, row, key, value)
    }
  }
}

///|
pub impl DynamicTableWriter for MetaTableWriter with fn exists(self, table, id) {
  (self.base).exists(table, id)
}

///|
pub impl DynamicTableWriter for MetaTableWriter with fn exists_by_key(self, table, key, value) {
  (self.base).exists_by_key(table, key, value)
}

///|
pub impl DynamicTableWriter for MetaTableWriter with fn columns(self, table, candidates) {
  (self.base).columns(table, candidates)
}

///|
pub extend MetaTableWriter with DynamicTableWriter::{
  columns, exists, exists_by_key, insert, update, update_by_key
}