import type { SplitJsonArrayOp, WhereRule } from '../transforms.schema';
import { isEmptyText, tryParseJsonArray, SOURCE_ROW_KEY, type Row } from '../row';

/**
 * split_json_array — JSON 数组字段拆行(等价 ClickHouse ARRAY JOIN + JSONExtract)
 *
 * 算法:
 *   1. 对每个父行 parent,取 array_field 字段(可能 stringified JSON 或已 parse 数组)
 *   2. 数组里每个元素 element → 生成一行子行 child:
 *        child[outputName] = parent[parentField]  (按 parent_keys 映射)
 *        child[outputName] = element[jsonPath]    (按 element_fields 映射)
 *   3. where 规则对**子行**做过滤(空数组/无效字段元素被剔除)
 *
 * 跳过场景(不抛错):
 *   - 父行 array_field 缺失 / 空字符串 / "[]" → 该父行不产任何子行
 *   - parse JSON 失败 → 计数 + warn,该父行跳过
 *   - 元素非对象(数组里塞了 string/number)→ 跳过
 */
export function runSplitJsonArray(
  op: SplitJsonArrayOp,
  rows: Row[],
): { rows: Row[]; skippedParents: number; skippedChildren: number } {
  const out: Row[] = [];
  let skippedParents = 0;
  let skippedChildren = 0;

  for (const parent of rows) {
    const arrRaw = parent[op.array_field];
    const arr = tryParseJsonArray(arrRaw);
    if (arr === null) {
      // parse 失败或非数组
      skippedParents++;
      continue;
    }
    for (const elem of arr) {
      if (typeof elem !== 'object' || elem === null) {
        skippedChildren++;
        continue;
      }
      const elemObj = elem as Record<string, unknown>;
      const child: Row = {};

      // 父行字段继承
      for (const [outputName, parentField] of Object.entries(op.parent_keys ?? {})) {
        child[outputName] = parent[parentField] ?? null;
      }

      // 元素字段提取(单层属性)
      for (const [outputName, jsonPath] of Object.entries(op.element_fields)) {
        child[outputName] = elemObj[jsonPath] ?? null;
      }

      // ⭐ __source_row 透传:子行继承父行的真原文 link
      // 拆出来的诊断/治疗子 fact 的 raw_payload = 父 EMR 完整原文(含 diag/treat_plan JSON 全貌)
      if (SOURCE_ROW_KEY in parent) {
        child[SOURCE_ROW_KEY] = parent[SOURCE_ROW_KEY];
      }

      // where 过滤
      if (op.where && !passWhere(child, op.where)) {
        skippedChildren++;
        continue;
      }

      out.push(child);
    }
  }

  return { rows: out, skippedParents, skippedChildren };
}

function passWhere(row: Row, where: Record<string, WhereRule>): boolean {
  for (const [field, rule] of Object.entries(where)) {
    const v = row[field];
    if ('not_empty' in rule) {
      if (isEmptyText(v)) return false;
    } else if ('in' in rule) {
      const set = new Set(rule.in.map((x) => String(x)));
      if (!set.has(String(v))) return false;
    } else if ('equals' in rule) {
      if (String(v) !== String(rule.equals)) return false;
    }
  }
  return true;
}
