// JeeflowFacade —— 45 action 统一入口(方案 §3.3;java JeeflowFacade 为契约源)
// D-M3-1:页查询仅在有 m_ 过滤时做 facade 级后过滤+重分页(rust 无条件重分页会打折扣
// 未过滤场景的 recordCount,与 java 五键语义冲突;此处对齐 java)。

///|
pub(all) struct Facade[R, E] {
  engine : @engine.Engine[R, E]
  repo : R
  ext_repo : E
}

///|
pub fn[R : @spi.ProcessRepository, E : @spi.ProcessExtRepository] Facade::make(
  ctx : @spi.Ctx[R, E]
) -> Facade[R, E] {
  let repo = ctx.repository()
  let ext = ctx.ext_repository()
  let engine = @engine.Engine::make(ctx)
  { engine, repo, ext_repo: ext }
}

///|
pub fn[R, E] Facade::engine(self : Facade[R, E]) -> @engine.Engine[R, E] {
  self.engine
}

///|
/// 统一入口:flow(action, args) → {code,msg,data}
pub async fn[R : @spi.ProcessRepository, E : @spi.ProcessExtRepository] Facade::flow(
  self : Facade[R, E],
  action : String,
  args : Map[String, Json]
) -> Json raise {
  let outcome = try? self.dispatch(action, args)
  match outcome {
    Ok(data) => transform_output(ok_data(data))
    Err(e) => transform_output(err(e.message()))
  }
}

///|
async fn[R : @spi.ProcessRepository, E : @spi.ProcessExtRepository] Facade::dispatch(
  self : Facade[R, E],
  action : String,
  args : Map[String, Json]
) -> Json raise @error.JeeflowError {
  match action {
    // ── 流程定义(8)──
    "processDefine/page" => self.define_page(args)
    "processDefine/detail" => self.define_detail(args)
    "processDefine/startAndExecute" => self.start_and_execute(args)
    "processDefine/deploy" => self.deploy(args)
    "processDefine/redeploy" => self.redeploy(args)
    "processDefine/remove" => self.define_remove(args)
    "processDefine/upAndDown" => self.define_up_and_down(args)
    "processDefine/getLastByName" => self.get_last_by_name(args)
    // ── 流程实例(14)──
    "processInstance/page" => self.instance_page(args)
    "processInstance/detail" => self.instance_detail(args)
    "processInstance/startAndExecute" => self.start_and_execute(args)
    "processInstance/withdraw" => self.withdraw(args)
    "processInstance/highLight" => self.high_light(args)
    "processInstance/approvalRecord" => self.approval_record(args)
    "processInstance/getAssigneeTextData" => self.get_assignee_text_data(args)
    "processInstance/bizData" => self.biz_data(args)
    "processInstance/createCCInstance" => self.create_cc_instance(args)
    "processInstance/updateCCStatus" => self.update_cc_status(args)
    "processInstance/ccList" => self.cc_list(args)
    "processInstance/stats/overview" => self.stats_overview(args)
    "processInstance/stats/trend" => self.stats_trend(args)
    "processInstance/stats/group" => self.stats_group(args)
    // ── 流程任务(9)──
    "processTask/todoList" => self.todo_list(args)
    "processTask/doneList" => self.done_list(args)
    "processTask/execute" => self.execute(args)
    "processTask/detail" => self.task_detail(args)
    "processTask/jumpAbleTaskNameList" => self.jump_able_task_name_list(args)
    "processTask/candidatePage" => self.candidate_page(args)
    "processTask/surrogate" => self.task_surrogate(args)
    "processTask/addCandidate" => self.task_surrogate(args)
    "processTask/latest" => self.task_latest(args)
    // ── 流程设计(9,需扩展仓储)──
    "processDesign/page" => self.design_page(args)
    "processDesign/detail" => self.design_detail(args)
    "processDesign/save" => self.design_save(args)
    "processDesign/update" => self.design_update(args)
    "processDesign/updateDefine" => self.design_update_define(args)
    "processDesign/remove" => self.design_remove(args)
    "processDesign/deploy" => self.design_deploy(args)
    "processDesign/redeploy" => self.design_redeploy(args)
    "processDesign/listByType" => self.design_list_by_type(args)
    // ── 委托代理(5,需扩展仓储)──
    "processSurrogate/page" => self.surrogate_page(args)
    "processSurrogate/save" => self.surrogate_save(args)
    "processSurrogate/update" => self.surrogate_update(args)
    "processSurrogate/detail" => self.surrogate_detail(args)
    "processSurrogate/remove" => self.surrogate_remove(args)
    _ => raise @error.UnknownAction(action)
  }
}

// ── 页查询公共(D-M3-1:有 m_ 过滤才后过滤+重分页)──

///|
pub fn facade_parse_m_filters(args : Map[String, Json]) -> Array[@model.QueryFilter] {
  let filters : Array[@model.QueryFilter] = []
  for key, value in args {
    if !key.starts_with("m_") {
      continue
    }
    let val_str = match value {
      Json::String(s) => s
      other => @json.stringify(other)
    }
    let rest = key.substring(start=2).to_string()
    let parts = split_char(rest, '_')
    if parts.length() >= 3 {
      let alias = parts[0]
      let op_str = parts[1]
      let column = camel_to_snake(join_char(parts, '_', 2))
      match @model.FilterOp::from_str(op_str) {
        Some(op) => filters.push(@model.QueryFilter::make(alias, op, column, val_str))
        None => ()
      }
    } else if parts.length() == 2 {
      let op_str = parts[0]
      if is_known_op(op_str) {
        match @model.FilterOp::from_str(op_str) {
          Some(op) => filters.push(@model.QueryFilter::make("t", op, camel_to_snake(parts[1]), val_str))
          None => ()
        }
      }
    }
  }
  filters
}

///|
fn split_char(s : String, sep : Char) -> Array[String] {
  let out : Array[String] = []
  let b = StringBuilder::new()
  for ch in s {
    if ch == sep {
      out.push(b.to_string())
      b.reset()
    } else {
      b.write_char(ch)
    }
  }
  out.push(b.to_string())
  out
}

///|
fn join_char(parts : Array[String], sep : Char, from : Int) -> String {
  let b = StringBuilder::new()
  for i in from.. from {
      b.write_char(sep)
    }
    b.write_string(parts[i])
  }
  b.to_string()
}

///|
fn camel_to_snake(s : String) -> String {
  let b = StringBuilder::new()
  for ch in s {
    let c = ch.to_int()
    if c >= 65 && c <= 90 {
      b.write_char('_')
      b.write_char(Char::from_int(c + 32))
    } else {
      b.write_char(ch)
    }
  }
  b.to_string()
}

///|
fn is_known_op(s : String) -> Bool {
  let upper = s.to_upper()
  upper == "EQ" ||
  upper == "NE" ||
  upper == "LIKE" ||
  upper == "LLIKE" ||
  upper == "RLIKE" ||
  upper == "GT" ||
  upper == "LT" ||
  upper == "GE" ||
  upper == "LE" ||
  upper == "IN" ||
  upper == "NIN" ||
  upper == "BT"
}

///|
/// 行级过滤(camelCase 行键;值按字符串比较,对齐 rust matches_filter)
fn matches_filter(row : Json, f : @model.QueryFilter) -> Bool {
  let camel_col = camel_to_snake_inverse_for(f)
  let field_val = match row.value(camel_col) {
    Some(v) => Some(v)
    None => row.value(f.column())
  }
  let field_str = match field_val {
    Some(Json::String(s)) => s
    Some(v) => @json.stringify(v)
    None => return false
  }
  match f.op() {
    Eq => field_str == f.value()
    Ne => field_str != f.value()
    Like => field_str.contains(f.value())
    Gt => field_str > f.value()
    Lt => field_str < f.value()
    Ge => field_str >= f.value()
    Le => field_str <= f.value()
    In => {
      let mut any = false
      for v in f.value().split(",") {
        if v.to_string() == field_str {
          any = true
        }
      }
      any
    }
    Nin => {
      let mut any = false
      for v in f.value().split(",") {
        if v.to_string() == field_str {
          any = true
        }
      }
      !any
    }
    Bt => {
      let parts = split_char(f.value(), ',')
      if parts.length() == 2 {
        field_str >= parts[0] && field_str <= parts[1]
      } else {
        false
      }
    }
  }
}

///|
/// 过滤列 → 行键:先 camelCase(行键已 camel),失败退原 snake
fn camel_to_snake_inverse_for(f : @model.QueryFilter) -> String {
  // column 本身是 snake_case(facade_parse_m_filters 已转),行键为 camelCase
  let b = StringBuilder::new()
  let mut upper_next = true // 别名拼接后的首字母大写由 to_camel 统一处理,这里列名整体 camel
  let _ = upper_next
  for ch in f.column() {
    if ch == '_' {
      upper_next = true
    } else if upper_next {
      let c = ch.to_int()
      if c >= 97 && c <= 122 {
        b.write_char(Char::from_int(c - 32))
      } else {
        b.write_char(ch)
      }
      upper_next = false
    } else {
      b.write_char(ch)
    }
  }
  b.to_string()
}

///|
/// 有过滤时的后过滤 + 重分页
fn apply_filters_and_repaginate(
  rows : Array[Json],
  filters : Array[@model.QueryFilter],
  page_num : Int64,
  page_size : Int64
) -> Array[Json] {
  if filters.is_empty() {
    return rows
  }
  let kept : Array[Json] = []
  for row in rows {
    let mut ok = true
    for f in filters {
      if !matches_filter(row, f) {
        ok = false
      }
    }
    if ok {
      kept.push(row)
    }
  }
  let start = ((page_num - 1L) * page_size).to_int()
  let out : Array[Json] = []
  if start >= 0 && start < kept.length() {
    let end = if start + page_size.to_int() < kept.length() {
      start + page_size.to_int()
    } else {
      kept.length()
    }
    for i in start.. Json,
  filters : Array[@model.QueryFilter],
  page_num : Int64,
  page_size : Int64
) -> Json {
  let rows_json : Array[Json] = []
  for r in page.rows() {
    rows_json.push(row_json(r))
  }
  let final_rows = apply_filters_and_repaginate(rows_json, filters, page_num, page_size)
  let total = if filters.is_empty() {
    page.record_count()
  } else {
    final_rows.length().to_int64()
  }
  let total_page = if total == 0L {
    0L
  } else {
    (total + page_size - 1L) / page_size
  }
  let obj = @json.empty_object()
  @json.obj_set(obj, "pageNum", @json.number_of(page_num))
  @json.obj_set(obj, "pageSize", @json.number_of(page_size))
  @json.obj_set(obj, "recordCount", @json.number_of(total))
  @json.obj_set(obj, "totalPage", @json.number_of(total_page))
  let rows = @json.empty_array()
  for r in final_rows {
    @json.arr_push(rows, r)
  }
  @json.obj_set(obj, "rows", rows)
  obj
}