// ServiceContext(MoonBit 形态)—— 移植自 rust context.rs + spi.rs 小 SPI

// 大 SPI 走泛型参数 [R, E];小 SPI 全闭包字段(方案 §3.2/§3.4.3)。

// ITransactionTemplate 口径(D-M1-1):Unit 形态——回调内写外部可变单元带回结果。

// MoonBit 0.10 无可靠 typealias,函数类型全部内联书写。



///|

/// IOrgUserProvider 闭包组(spec/05 v1.6.0)

pub(all) struct OrgUserProviderFns {

  find_dept_leaders : (async (String) -> Array[String] raise @error.JeeflowError)

  find_dept_main_leaders : (async (String) -> Array[String] raise @error.JeeflowError)

  find_by_role : (async (String) -> Array[String] raise @error.JeeflowError)

}



///|

/// IUserSearchProvider 闭包组(spec/06 §4.3/§6)

pub(all) struct UserSearchProviderFns {

  page : (async (@model.PageQuery) -> @model.PageResult[Map[String, Json]] raise @error.JeeflowError)

  find_by_id : (async (String) -> Map[String, Json]? raise @error.JeeflowError)

}



///|

/// IJsonProvider 闭包组(spec/05)

pub(all) struct JsonProviderFns {

  to_json : (Json) -> String

  from_json : (String) -> Json raise @error.JeeflowError

  is_json : (String) -> Bool

}



///|

/// 默认 JsonProvider(core stdlib @cjson 承载)

pub fn default_json_provider() -> JsonProviderFns {

  {

    to_json: fn(v) { v.stringify() },

    from_json: (s) => @json.parse_json(s),

    is_json: fn(s) { @cjson.valid(s) },

  }

}



///|

/// IActionPermissionProvider 默认实现:wf:{action /→:}(spec/06 §2.6)

pub fn default_action_permission_provider(action : String) -> Array[String] {

  ["wf:\{action.replace(old="/", new=":")}"]

}



///|

/// FlowInterceptor 闭包形态(带 order)

pub(all) struct InterceptorFn {

  order : Int

  run : (async (@exec.Execution) -> Unit raise @error.JeeflowError)

}



///|

/// ServiceContext —— 启动期构建、运行期只读的 SPI 注册表

pub(all) struct Ctx[R, E] {

  repository : R

  ext_repository : E

  // ── 小 SPI(闭包字段)──

  mut user_provider : ((async (String) -> @model.UserInfo? raise @error.JeeflowError))?

  mut org_user_provider : OrgUserProviderFns?

  mut user_search_provider : UserSearchProviderFns?

  mut json_provider : JsonProviderFns?

  mut expression_evaluator : ((String, Map[String, Json]) -> Json raise @error.JeeflowError)?

  mut id_generator : (() -> Int64 raise @error.JeeflowError)?

  mut action_permission_provider : ((String) -> Array[String])?

  mut biz_data_reader : ((async (String, Int64) -> Map[String, Json]? raise @error.JeeflowError))?

  // ── 按名注册表 ──

  assignment_handlers : Map[String, (async (@exec.Execution) -> String raise @error.JeeflowError)]

  decision_handlers : Map[String, (async (@exec.Execution) -> String raise @error.JeeflowError)]

  /// 记录类(`snaker:custom`)节点处理器(issues/142 A 批 · spec/02 §6.1/§6.2)。

  /// 键=流程定义 `properties.clazz` 的**原样字符串**:MoonBit 没有反射,java 那句

  /// `Class.forName(clazz)` 在本栈无从谈起 ⇒ 与 C#/Python 栈同策「按名注册、按名解析」,

  /// 由集成方把 JVM 类名/任意自定义串映射到闭包(共享夹具 `flows/08-custom-node.json`

  /// 里就是 `com.mldong.jeeflow.test.TestCustomHandler`)。

  /// 返回值取 `Json`:本栈流程变量的载体就是 Json(`@json.FlowData`),处理器无返回值时

  /// 返回 `@json.null_json()` ⇒ 引擎不写键(对齐 python「ret is not None 才写」)。

  custom_handlers : Map[String, (async (@exec.Execution) -> Json raise @error.JeeflowError)]

  /// 按名拦截器注册表(audit8 R91,键形按丙=java 类全名串,与 `assignment_handlers`/

  /// `custom_handlers` 同一惯例):流程定义里 `preInterceptors`/`postInterceptors` 声明的

  /// 每一项都要在这一档查得到实体——MoonBit 没有反射,java 那句 `Class.forName(className)`

  /// 在本栈无从执行 ⇒ 实体只能由宿主按名给。查不到由引擎显式报错(issues/60 纪律,不静默跳过)。

  named_interceptors : Map[String, InterceptorFn]

  // ── 有序集合 ──

  interceptors : Array[InterceptorFn]

  /// 前置扩展位(issues/116 批次 D):与 `interceptors` **分开注册、分开触发**。

  /// 本栈 `InterceptorFn` 只有一个 `run`(无 before/after 之分),若在 `fire_pre_interceptors`

  /// 里复用同一数组,只在后置注册的既有实现(persist 的 PersistPostInterceptor)会被

  /// 双触发(归档/SYNC 写两遍)。故 pre 另起一条通道,pre 体系的整体 canonical 形态

  /// (含第三方自写拦截器的 pre 语义)仍归 issues/104 §P1。

  pre_interceptors : Array[InterceptorFn]

  event_listeners : Array[((async (@event.ProcessEvent) -> Unit raise @error.JeeflowError))]

  /// 委托自动生效开关(issues/116 批次 D / spec 06 §4.5 条款 3):**默认开启**

  /// (`Ctx::new` 里给 true,全栈唯一构造入口,不存在"零值即关闭"的旁路)。

  mut surrogate_auto_apply : Bool

}



///|

pub fn[R, E] Ctx::new(repository : R, ext_repository : E) -> Ctx[R, E] {

  {

    repository,

    ext_repository,

    user_provider: None,

    org_user_provider: None,

    user_search_provider: None,

    json_provider: Some(default_json_provider()),

    expression_evaluator: None,

    id_generator: None,

    action_permission_provider: None,

    biz_data_reader: None,

    assignment_handlers: Map([]),

    decision_handlers: Map([]),

    custom_handlers: Map([]),

    named_interceptors: Map([]),

    interceptors: [],

    pre_interceptors: [],

    event_listeners: [],

    surrogate_auto_apply: true,

  }

}



///|

pub fn[R, E] Ctx::repository(self : Ctx[R, E]) -> R {

  self.repository

}



///|

pub fn[R, E] Ctx::ext_repository(self : Ctx[R, E]) -> E {

  self.ext_repository

}



///|

/// 请求级派生:**新建一份**注册表,只替换两个仓储端点。

/// ⚠ 不能照下面那批 `with_*` 的样式加"换仓储"的方法——本栈结构体是**引用语义**(本轮实测:

/// `let b = a` 之后写 `b.mut_field`/`b.arr.push` 都会改到 `a`;`Map` 赋值同样共享底层,

/// 只有显式 `.copy()` 才切开),而下面那批 `with_*` 全是"原地改 `self` 再返回 `self`"。

/// 若照那个样式加 `with_repository`,请求 A 换上去的仓储会对所有请求可见

/// ⇒ issues/144 的"跨请求共用事务句柄"只是换个载体重演,而且更隐蔽(编译期与门禁都看不出来)。

/// 容器逐个 `copy()` ⇒ 派生体后续的运行期注册不回写母体;

/// **闭包字段按引用带过来**是刻意的:id 生成器必须共享同一个实例——雪花 `worker_id` 固定、

/// `sequence` 每毫秒归零,两个请求各自 new 一个生成器会在同一毫秒撞出同一个 id ⇒ 主键冲突。

/// ⚠ 纪律:SPI 注册只允许启动期做(见 struct 头注"启动期构建、运行期只读")。

/// 运行期再 `register_*` 只落进当前这一份,跨请求不可见——这是派生的语义,不是 bug。

pub fn[R, E] Ctx::for_request(

  self : Ctx[R, E],

  repository : R,

  ext_repository : E

) -> Ctx[R, E] {

  {

    repository,

    ext_repository,

    user_provider: self.user_provider,

    org_user_provider: self.org_user_provider,

    user_search_provider: self.user_search_provider,

    json_provider: self.json_provider,

    expression_evaluator: self.expression_evaluator,

    id_generator: self.id_generator,

    action_permission_provider: self.action_permission_provider,

    biz_data_reader: self.biz_data_reader,

    assignment_handlers: self.assignment_handlers.copy(),

    decision_handlers: self.decision_handlers.copy(),

    custom_handlers: self.custom_handlers.copy(),

    named_interceptors: self.named_interceptors.copy(),

    interceptors: self.interceptors.copy(),

    pre_interceptors: self.pre_interceptors.copy(),

    event_listeners: self.event_listeners.copy(),

    surrogate_auto_apply: self.surrogate_auto_apply,

  }

}

///|

pub fn[R, E] Ctx::with_user_provider(

  self : Ctx[R, E],

  p : (async (String) -> @model.UserInfo? raise @error.JeeflowError)

) -> Ctx[R, E] {

  self.user_provider = Some(p)

  self

}



///|

pub fn[R, E] Ctx::with_org_user_provider(self : Ctx[R, E], p : OrgUserProviderFns) -> Ctx[R, E] {

  self.org_user_provider = Some(p)

  self

}



///|

pub fn[R, E] Ctx::with_user_search_provider(self : Ctx[R, E], p : UserSearchProviderFns) -> Ctx[R, E] {

  self.user_search_provider = Some(p)

  self

}



///|

pub fn[R, E] Ctx::with_expression_evaluator(

  self : Ctx[R, E],

  f : (String, Map[String, Json]) -> Json raise @error.JeeflowError

) -> Ctx[R, E] {

  self.expression_evaluator = Some(f)

  self

}



///|

pub fn[R, E] Ctx::with_id_generator(

  self : Ctx[R, E],

  f : () -> Int64 raise @error.JeeflowError

) -> Ctx[R, E] {

  self.id_generator = Some(f)

  self

}



///|

pub fn[R, E] Ctx::with_action_permission_provider(

  self : Ctx[R, E],

  f : (String) -> Array[String]

) -> Ctx[R, E] {

  self.action_permission_provider = Some(f)

  self

}



///|

pub fn[R, E] Ctx::with_biz_data_reader(

  self : Ctx[R, E],

  f : (async (String, Int64) -> Map[String, Json]? raise @error.JeeflowError)

) -> Ctx[R, E] {

  self.biz_data_reader = Some(f)

  self

}



///|

/// 委托自动生效开关(issues/116 批次 D / spec 06 §4.5 条款 3):**默认开启**

/// (`Ctx::new` 里置 true,本栈 `Ctx` 仅此一个构造函数,无"零值即关闭"旁路)。

/// 传 `false` 即显式关闭,行为回到"只有 `processSurrogate/*` 台账 CRUD、建单不并入代理人"。

/// 引擎侧消费点见 `engine/surrogate.mbt` 的 `apply_surrogate`。

pub fn[R, E] Ctx::with_surrogate_auto_apply(self : Ctx[R, E], on : Bool) -> Ctx[R, E] {

  self.surrogate_auto_apply = on

  self

}



///|

/// 前置拦截器注册(issues/116 批次 D 补的最小挂点):与 `register_interceptor`(后置)

/// 分开的通道,避免把既有只在后置注册的实现变成双触发。

/// ⚠️ pre 体系的整体 canonical 形态归 issues/104 §P1,本轮只打通"有地方挂"这一步。

pub fn[R, E] Ctx::register_pre_interceptor(self : Ctx[R, E], i : InterceptorFn) -> Unit {

  self.pre_interceptors.push(i)

  self.pre_interceptors.sort_by(fn(a, b) { a.order.compare(b.order) })

}



///|

pub fn[R, E] Ctx::json_provider_or_default(self : Ctx[R, E]) -> JsonProviderFns {

  match self.json_provider {

    Some(p) => p

    None => default_json_provider()

  }

}



///|

/// IIdGenerator:未注入时回退默认雪花(worker 0)

pub fn[R, E] Ctx::id_generator_or_default(

  self : Ctx[R, E]

) -> () -> Int64 raise @error.JeeflowError {

  match self.id_generator {

    Some(f) => f

    None => {

      let gen = @id_gen.DefaultIdGenerator::new(0L)

      fn() { gen.next_id() }

    }

  }

}



///|

/// IActionPermissionProvider:未注入用默认 wf:{action /→:}

pub fn[R, E] Ctx::permission_codes(self : Ctx[R, E], action : String) -> Array[String] {

  match self.action_permission_provider {

    Some(f) => (f)(action)

    None => default_action_permission_provider(action)

  }

}



///|

pub fn[R, E] Ctx::register_assignment_handler(

  self : Ctx[R, E],

  name : String,

  handler : (async (@exec.Execution) -> String raise @error.JeeflowError)

) -> Unit {

  self.assignment_handlers[name] = handler

}



///|

pub fn[R, E] Ctx::register_decision_handler(

  self : Ctx[R, E],

  name : String,

  handler : (async (@exec.Execution) -> String raise @error.JeeflowError)

) -> Unit {

  self.decision_handlers[name] = handler

}



///|

pub fn[R, E] Ctx::register_interceptor(self : Ctx[R, E], i : InterceptorFn) -> Unit {

  self.interceptors.push(i)

  self.interceptors.sort_by(fn(a, b) { a.order.compare(b.order) })

}



///|

/// 按名注册拦截器(audit8 R91):`name`=流程定义声明里的原样串(java 类全名),

/// 与 `register_interceptor`(引擎级列表)是两档——声明档存在时只走本表,两档不叠加。

pub fn[R, E] Ctx::register_named_interceptor(

  self : Ctx[R, E], name : String, i : InterceptorFn

) -> Unit {

  self.named_interceptors[name] = i

}



///|

/// 按名查拦截器实体(只查表;查不到由调用方报错,本函数不缓存错误⇒每次真查)

pub fn[R, E] Ctx::named_interceptor(self : Ctx[R, E], name : String) -> InterceptorFn? {

  self.named_interceptors.get(name)

}



///|

pub fn[R, E] Ctx::register_event_listener(self : Ctx[R, E], l : (async (@event.ProcessEvent) -> Unit raise @error.JeeflowError)) -> Unit {

  self.event_listeners.push(l)

}



///|

pub fn[R, E] Ctx::event_listeners(self : Ctx[R, E]) -> Array[((async (@event.ProcessEvent) -> Unit raise @error.JeeflowError))] {

  self.event_listeners

}



///|

pub fn[R, E] Ctx::find_assignment_handler(

  self : Ctx[R, E],

  name : String

) -> ((async (@exec.Execution) -> String raise @error.JeeflowError))? {

  self.assignment_handlers.get(name)

}



///|

pub fn[R, E] Ctx::find_decision_handler(

  self : Ctx[R, E],

  name : String

) -> ((async (@exec.Execution) -> String raise @error.JeeflowError))? {

  self.decision_handlers.get(name)

}



///|

/// 注册记录类节点处理器(issues/142 A 批)。键=定义里的 `clazz` 原样串,不做归一化

/// (大小写/前缀归一是 issues/141 G4 义务 1 的 C 方案,立法在前、实现另批,这里不抢跑)。

pub fn[R, E] Ctx::register_custom_handler(

  self : Ctx[R, E],

  clazz : String,

  handler : (async (@exec.Execution) -> Json raise @error.JeeflowError)

) -> Unit {

  self.custom_handlers[clazz] = handler

}



///|

/// 按 `clazz` 解析记录类节点处理器;未注册 ⇒ `None`(**引擎侧记日志后照常落历史行并续流,

/// 不打断建单**,spec/02 §6.2 第 2 条;处理器**自身**执行失败不在豁免内,照旧外抛)。

pub fn[R, E] Ctx::find_custom_handler(

  self : Ctx[R, E],

  clazz : String

) -> ((async (@exec.Execution) -> Json raise @error.JeeflowError))? {

  if clazz.is_empty() {

    None

  } else {

    self.custom_handlers.get(clazz)

  }

}





///|

/// 同步实现的 async 桥(issues/146):内存夹具/demo/宿主纯计算实现,复用同步闭包,

/// 免得每个注册点写一条空转 async 闭包——本栈编译器会把"async 但体内无 await"判成

/// unused_async 警告(实测:闭包字面量与 fn 声明都判,无抑制属性可摘),

/// 集中到这一处 ⇒ 注册点零警告,代价只有桥自身这 3 条被判决。

/// ⚠ 别把这句话当成"警告零增长":本轮实测全仓警告 22→37,其中 +7 unused_async(三条桥 + 少数

///    "只传播不 raise"之外的空转 async 位)、+8 unused_error_type(7 支 `notify_*` 与 `@event.notify`

///    必须带闭集 raise 标注,否则错误集塌成开放 Error 并外溢)。新基线与逐码归因见 MAINTAINING §发布闸门。

/// 真查库的宿主实现直接写 `async fn(id) raise @error.JeeflowError { ... }`,不经本桥。

pub fn[R, A] async_of_sync(

  f : (A) -> R raise @error.JeeflowError

) -> (async (A) -> R raise @error.JeeflowError) {

  async fn(a) raise @error.JeeflowError { f(a) }

}



///|

/// 两参档(biz_data_reader 的 (table, id))

pub fn[R, A, B] async_of_sync2(

  f : (A, B) -> R raise @error.JeeflowError

) -> (async (A, B) -> R raise @error.JeeflowError) {

  async fn(a, b) raise @error.JeeflowError { f(a, b) }

}



///|

/// 事件监听器档:既有监听器错误集是开放 `raise`(单监听器异常由 @event.notify 兜底隔离),

/// 故另开一支桥,错误集不折成 JeeflowError。

pub fn sync_listener(

  f : (@event.ProcessEvent) -> Unit raise @error.JeeflowError

) -> (async (@event.ProcessEvent) -> Unit raise @error.JeeflowError) {

  async fn(e) raise @error.JeeflowError { f(e) }

}







///|

/// ── 同步形状便捷口(issues/146)──

/// 供数/回调族 SPI 的正式形状是 async(宿主现场查库);纯内存实现(demo、仓内夹具、

/// 以及只做字符串运算的 handler)走下面这组 `*_sync` 便捷口,内部统一经 async_of_sync 桥。

/// 两条通道注册完的 Ctx 字段类型完全相同,引擎只认 async 那一种。

pub(all) struct OrgUserProviderFnsSync {

  find_dept_leaders : (String) -> Array[String] raise @error.JeeflowError

  find_dept_main_leaders : (String) -> Array[String] raise @error.JeeflowError

  find_by_role : (String) -> Array[String] raise @error.JeeflowError

}



///|

pub fn OrgUserProviderFnsSync::to_async(self : OrgUserProviderFnsSync) -> OrgUserProviderFns {

  {

    find_dept_leaders: async_of_sync(self.find_dept_leaders),

    find_dept_main_leaders: async_of_sync(self.find_dept_main_leaders),

    find_by_role: async_of_sync(self.find_by_role),

  }

}



///|

pub(all) struct UserSearchProviderFnsSync {

  page : (@model.PageQuery) -> @model.PageResult[Map[String, Json]] raise @error.JeeflowError

  find_by_id : (String) -> Map[String, Json]? raise @error.JeeflowError

}



///|

pub fn UserSearchProviderFnsSync::to_async(

  self : UserSearchProviderFnsSync

) -> UserSearchProviderFns {

  {

    page: async_of_sync(self.page),

    find_by_id: async_of_sync(self.find_by_id),

  }

}



///|

pub fn[R, E] Ctx::with_user_provider_sync(

  self : Ctx[R, E],

  p : (String) -> @model.UserInfo? raise @error.JeeflowError

) -> Ctx[R, E] {

  self.with_user_provider(async_of_sync(p))

}



///|

pub fn[R, E] Ctx::with_org_user_provider_sync(

  self : Ctx[R, E],

  p : OrgUserProviderFnsSync

) -> Ctx[R, E] {

  self.with_org_user_provider(p.to_async())

}



///|

pub fn[R, E] Ctx::with_user_search_provider_sync(

  self : Ctx[R, E],

  p : UserSearchProviderFnsSync

) -> Ctx[R, E] {

  self.with_user_search_provider(p.to_async())

}



///|

pub fn[R, E] Ctx::with_biz_data_reader_sync(

  self : Ctx[R, E],

  f : (String, Int64) -> Map[String, Json]? raise @error.JeeflowError

) -> Ctx[R, E] {

  self.with_biz_data_reader(async_of_sync2(f))

}



///|

pub fn[R, E] Ctx::register_event_listener_sync(

  self : Ctx[R, E],

  l : (@event.ProcessEvent) -> Unit raise @error.JeeflowError

) -> Unit {

  self.register_event_listener(sync_listener(l))

}



///|

pub fn[R, E] Ctx::register_assignment_handler_sync(

  self : Ctx[R, E],

  name : String,

  handler : (@exec.Execution) -> String raise @error.JeeflowError

) -> Unit {

  self.register_assignment_handler(name, async_of_sync(handler))

}



///|

pub fn[R, E] Ctx::register_decision_handler_sync(

  self : Ctx[R, E],

  name : String,

  handler : (@exec.Execution) -> String raise @error.JeeflowError

) -> Unit {

  self.register_decision_handler(name, async_of_sync(handler))

}



///|

pub fn[R, E] Ctx::register_custom_handler_sync(

  self : Ctx[R, E],

  clazz : String,

  handler : (@exec.Execution) -> Json raise @error.JeeflowError

) -> Unit {

  self.register_custom_handler(clazz, async_of_sync(handler))

}



///|

/// 拦截器的同步档:只补 `run` 的形状,order 与排序语义不变。

pub fn[R, E] Ctx::register_interceptor_sync(

  self : Ctx[R, E],

  order : Int,

  run : (@exec.Execution) -> Unit raise @error.JeeflowError

) -> Unit {

  self.register_interceptor({ order, run: async_of_sync(run) })

}



///|

pub fn[R, E] Ctx::register_pre_interceptor_sync(

  self : Ctx[R, E],

  order : Int,

  run : (@exec.Execution) -> Unit raise @error.JeeflowError

) -> Unit {

  self.register_pre_interceptor({ order, run: async_of_sync(run) })

}