// jeeflow-persist —— 移植自 rust jeeflow-persist/src/lib.rs
// DynamicTableWriter + PersistPostInterceptor(ARCHIVE/SYNC,spec/09)+ 元数据 + 表名安全。

///|
/// 字段元数据(动态表列)
pub(all) struct FieldMeta {
  mut column_name : String
  mut column_type : String
  mut display_name : String
  mut nullable : Bool
  mut default_value : String?
  mut permission : Int // 1 只读 / 2 可编辑 / 3 隐藏
}

///|
pub fn FieldMeta::make(column_name : String, column_type : String, display_name : String) -> FieldMeta {
  {
    column_name,
    column_type,
    display_name,
    nullable: true,
    default_value: None,
    permission: 2,
  }
}

///|
pub fn FieldMeta::is_editable(self : FieldMeta) -> Bool {
  self.permission == 2
}

///|
pub fn FieldMeta::is_visible(self : FieldMeta) -> Bool {
  self.permission != 3
}

///|
pub(all) struct TableMeta {
  mut table_name : String
  mut display_name : String
  mut fields : Array[FieldMeta]
}

///|
pub fn TableMeta::make(table_name : String, display_name : String) -> TableMeta {
  { table_name, display_name, fields: [] }
}

///|
pub fn TableMeta::add_field(self : TableMeta, field : FieldMeta) -> Unit {
  self.fields.push(field)
}

///|
pub fn TableMeta::get_field(self : TableMeta, column_name : String) -> FieldMeta? {
  let mut found : FieldMeta? = None
  for f in self.fields {
    if f.column_name == column_name {
      found = Some(f)
    }
  }
  found
}

///|
/// IDynamicMetaProvider 内存实现
pub(all) struct InMemoryMetaProvider {
  tables : Map[String, TableMeta]
}

///|
pub fn InMemoryMetaProvider::new() -> InMemoryMetaProvider {
  { tables: Map([]) }
}

///|
pub fn InMemoryMetaProvider::register(self : InMemoryMetaProvider, meta : TableMeta) -> Unit {
  self.tables[meta.table_name] = meta
}

///|
pub fn InMemoryMetaProvider::get_table_meta(
  self : InMemoryMetaProvider,
  table_name : String
) -> TableMeta? {
  self.tables.get(table_name)
}

///|
pub fn InMemoryMetaProvider::list_table_names(self : InMemoryMetaProvider) -> Array[String] {
  let out : Array[String] = []
  for k, _ in self.tables {
    out.push(k)
  }
  out
}

///|
let reserved_prefixes : Array[String] = [
  "sys_", "wf_", "mysql.", "information_schema.", "pg_", "sqlite_",
]

///|
/// 动态表名安全检查:仅字母数字下划线 + 保留前缀拒绝
pub fn is_table_name_safe(name : String) -> Bool {
  if name.is_empty() {
    return false
  }
  for ch in name {
    let c = ch.to_int()
    let alnum = (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122)
    if !(alnum || ch == '_') {
      return false
    }
  }
  let lower = name.to_lower()
  for prefix in reserved_prefixes {
    if lower.has_prefix(prefix) {
      return false
    }
  }
  true
}

///|
/// 持久化模式
pub enum PersistMode {
  Archive
  Sync
} derive(Eq, Debug)

///|
pub fn PersistMode::from_str(s : String) -> PersistMode? {
  match s.to_upper() {
    "ARCHIVE" => Some(Archive)
    "SYNC" => Some(Sync)
    _ => None
  }
}

///|
/// DynamicTableWriter trait(MoonBit:大接口 trait 形态)
///
/// issues/149:六法全部 async —— 本栈写真库只有 moonmysql 的 async 连接这一条路,同步形状
/// 发不出查询(145-6 记的机理)。async 由 **trait 声明**承载:`impl … with fn` 处不重复写
/// `async`(实测写了判 parse error),⇒ `InMemoryTableWriter` 六支 impl 与 `&DynamicTableWriter`
/// 的六处调用点都不用改;唯一要跟着改的是同步函数里调 writer 的 `put_state_field`。
/// 同形参照=`core/spi/repository.mbt` 的 `ProcessRepository`(async trait + repository-mysql 真库实现)。
/// ⚠ 列探测(`columns`)归属裁决=**留在 writer**:参考实现 java
/// `persist/DynamicTableWriter.java:20` 的 `filterColumns` 就挂在 writer 上,而
/// `IDynamicMetaProvider` 管字段权限/显示元数据——两者在 java 本就并存,故不并入
/// `DynamicMetaProviderFns`(146 缺口二),也不留"同一件事两个端口"。
pub(open) trait DynamicTableWriter {
  async fn insert(Self, String, Map[String, Json]) -> Int64 raise @error.JeeflowError
  async fn update(Self, String, Int64, Map[String, Json]) -> Unit raise @error.JeeflowError
  async fn exists(Self, String, Int64) -> Bool raise @error.JeeflowError
  async fn exists_by_key(Self, String, String, Int64) -> Bool raise @error.JeeflowError
  async fn update_by_key(Self, String, Map[String, Json], String, Int64) -> Unit raise @error.JeeflowError
  // 探测实际存在的列(状态字段 {节点ID}_{状态码}/{节点ID} 过滤);缺省全保留
  async fn columns(Self, String, Array[String]) -> Array[String] raise @error.JeeflowError
}

///|
/// 系统字段填充(时间走 Clock 注入口,D-M0-3)
pub fn fill_system_fields(
  data : Map[String, Json],
  operator : String,
  is_insert : Bool
) -> Unit {
  let now = @model.current_time_str()
  if is_insert {
    data["create_time"] = @json.string_of(now)
    data["create_user"] = @json.string_of(operator)
  }
  data["update_time"] = @json.string_of(now)
  data["update_user"] = @json.string_of(operator)
}


// ── 显式 extend(implicit_impl_as_method 弃办:impl 的方法不再被隐式提升为常规方法)──
// 由 scripts/moon_warn_w2.py 从编译器消息里生成;一行一个 (类型, trait),可 diff 可复审。
pub extend PersistMode with Eq::{equal, not_equal}
pub extend PersistMode with Debug::{to_repr}


///|
/// IDynamicMetaProvider 的 MoonBit 同位(issues/146 缺口二)。
/// java 侧这个端口就定义在 jeeflow-persist/meta(不在 core),消费点是 MetaTableWriter/Reader
/// ⇒ 本栈同位同样落在 persist:归档/SYNC 的写侧与回显读侧共用一份元数据(storageType 语义两侧一致)。
/// load_table_meta 是 async:集成方的元数据来源是库(mldong dev_schema / information_schema),
/// 同步形状就会重演缺口一——启动期快照、改表结构引擎看不见。
/// 未注入 ⇒ 引擎行为与既往一致(InMemory 档照常,缺省不启用外部元数据)。
pub(all) struct DynamicMetaProviderFns {
  load_table_meta : (async (String) -> TableMeta? raise @error.JeeflowError)
}

///|
/// 内存档 → 端口(纯内存读 ⇒ 经 @spi.async_of_sync 桥,注册点零警告)
pub fn InMemoryMetaProvider::as_provider(self : InMemoryMetaProvider) -> DynamicMetaProviderFns {
  let it = self
  let f : (String) -> TableMeta? raise @error.JeeflowError = name => it.get_table_meta(name)
  { load_table_meta: @spi.async_of_sync(f) }
}